├── client ├── easierStorage.js ├── index.html ├── mocha │ ├── mocha.css │ └── mocha.js └── easierStorage_test.js ├── LICENSE ├── README.md └── src └── easierStorage.js /client/easierStorage.js: -------------------------------------------------------------------------------- 1 | ../src/easierStorage.js -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This software is available under version 2.0 of the MPL: 2 | 3 | https://www.mozilla.org/MPL/ 4 | -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test! 6 | 7 | 8 | 9 | 10 | 15 | 16 | 17 | 18 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # easierStorage 2 | easierStorage makes it dead simple to work with tree structures that are stored in localStorage. 3 | 4 | ## Examples 5 | ``` 6 | // Write an item 3 levels deep without ever checking whether top or middle 7 | // exist. calling setItem like this will automatically create any nodes that 8 | // are needed. 9 | easierStorage.setItem("top", "middle", "leaf", "oooh!"); 10 | 11 | // Write into a new branch off of the top, this will not destroy the middle 12 | // branch. 13 | easierStorage.setItem("top", "right", "end", "wee!"); 14 | 15 | // Go directly to the item you want to get, without checking whether top or 16 | // middle exist. Returns undefined if any nodes do not exist. 17 | var leafValue = easierStorage.getItem("top", "middle", "leaf"); 18 | // leafValue === "oooh!" 19 | 20 | 21 | ``` 22 | ## License: 23 | Mozilla MPL 2.0 24 | 25 | ## Author 26 | * Shane Tomlinson 27 | * @shane_tomlinson 28 | * shane@shanetomlinson.com 29 | * set117@yahoo.com 30 | * stomlinson@mozilla.com 31 | * http://shanetomlinson.com 32 | 33 | -------------------------------------------------------------------------------- /client/mocha/mocha.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; 4 | padding: 60px 50px; 5 | } 6 | 7 | #mocha h1, h2 { 8 | margin: 0; 9 | } 10 | 11 | #mocha h1 { 12 | margin-top: 15px; 13 | font-size: 1em; 14 | font-weight: 200; 15 | } 16 | 17 | #mocha .suite .suite h1 { 18 | margin-top: 0; 19 | font-size: .8em; 20 | } 21 | 22 | #mocha h2 { 23 | font-size: 12px; 24 | font-weight: normal; 25 | cursor: pointer; 26 | } 27 | 28 | #mocha .suite { 29 | margin-left: 15px; 30 | } 31 | 32 | #mocha .test { 33 | margin-left: 15px; 34 | } 35 | 36 | #mocha .test:hover h2::after { 37 | position: relative; 38 | top: 0; 39 | right: -10px; 40 | content: '(view source)'; 41 | font-size: 12px; 42 | font-family: arial; 43 | color: #888; 44 | } 45 | 46 | #mocha .test.pending:hover h2::after { 47 | content: '(pending)'; 48 | font-family: arial; 49 | } 50 | 51 | #mocha .test.pass::before { 52 | content: '✓'; 53 | font-size: 12px; 54 | display: block; 55 | float: left; 56 | margin-right: 5px; 57 | color: #00c41c; 58 | } 59 | 60 | #mocha .test.pending { 61 | color: #0b97c4; 62 | } 63 | 64 | #mocha .test.pending::before { 65 | content: '◦'; 66 | color: #0b97c4; 67 | } 68 | 69 | #mocha .test.fail { 70 | color: #c00; 71 | } 72 | 73 | #mocha .test.fail pre { 74 | color: black; 75 | } 76 | 77 | #mocha .test.fail::before { 78 | content: '✖'; 79 | font-size: 12px; 80 | display: block; 81 | float: left; 82 | margin-right: 5px; 83 | color: #c00; 84 | } 85 | 86 | #mocha .test pre.error { 87 | color: #c00; 88 | } 89 | 90 | #mocha .test pre { 91 | display: inline-block; 92 | font: 12px/1.5 monaco, monospace; 93 | margin: 5px; 94 | padding: 15px; 95 | border: 1px solid #eee; 96 | border-bottom-color: #ddd; 97 | -webkit-border-radius: 3px; 98 | -webkit-box-shadow: 0 1px 3px #eee; 99 | } 100 | 101 | #error { 102 | color: #c00; 103 | font-size: 1.5 em; 104 | font-weight: 100; 105 | letter-spacing: 1px; 106 | } 107 | 108 | #stats { 109 | position: fixed; 110 | top: 15px; 111 | right: 10px; 112 | font-size: 12px; 113 | margin: 0; 114 | color: #888; 115 | } 116 | 117 | #stats .progress { 118 | float: right; 119 | padding-top: 0; 120 | } 121 | 122 | #stats em { 123 | color: black; 124 | } 125 | 126 | #stats li { 127 | display: inline-block; 128 | margin: 0 5px; 129 | list-style: none; 130 | padding-top: 11px; 131 | } 132 | 133 | code .comment { color: #ddd } 134 | code .init { color: #2F6FAD } 135 | code .string { color: #5890AD } 136 | code .keyword { color: #8A6343 } 137 | code .number { color: #2F6FAD } 138 | -------------------------------------------------------------------------------- /src/easierStorage.js: -------------------------------------------------------------------------------- 1 | (function(exports) { 2 | "use strict"; 3 | 4 | function setItem() { 5 | var args = [].slice.call(arguments, 0), 6 | rootname = args[0], 7 | value = args.pop(), 8 | obj, 9 | root, 10 | len = args.length; 11 | 12 | if(!len) { 13 | throw "setItem must be called with at least one key and a value"; 14 | } 15 | else { 16 | root = obj = JSON.parse(localStorage[rootname] || "{}"); 17 | 18 | if(len === 1) { 19 | root = value; 20 | } 21 | else { 22 | for(var index = 1, key, max = len - 1; key = args[index]; ++index) { 23 | if(index === max) { 24 | obj[key] = value; 25 | } 26 | else { 27 | obj = obj[key] = obj[key] || {}; 28 | } 29 | } 30 | } 31 | 32 | localStorage[rootname] = JSON.stringify(root); 33 | } 34 | } 35 | 36 | function getItem(key) { 37 | var args = [].slice.call(arguments, 0), 38 | rootname = args[0], 39 | len = args.length, 40 | max = len - 1; 41 | 42 | if(len === 0) { 43 | throw "getItem must be called with at least one key"; 44 | } 45 | else if(len === 1) { 46 | var value = localStorage[rootname], 47 | undef; 48 | return (typeof value !== "undefined" && value !== null) ? JSON.parse(value) : undef; 49 | } 50 | else { 51 | var obj = JSON.parse(localStorage[rootname] || "{}"); 52 | 53 | for(var index = 1, key; obj && (key = args[index]); ++index) { 54 | if(index === max) { 55 | return obj[key]; 56 | } 57 | else { 58 | obj = obj[key]; 59 | } 60 | } 61 | } 62 | } 63 | 64 | function removeItem() { 65 | var args = [].slice.call(arguments, 0), 66 | rootname = args[0], 67 | len = args.length; 68 | 69 | if(len === 0) { 70 | throw "removeItem must be called with at least one key"; 71 | } 72 | else if(len === 1) { 73 | localStorage.removeItem(rootname); 74 | } 75 | else { 76 | var root = JSON.parse(localStorage[rootname] || "{}"), 77 | obj = root; 78 | 79 | for(var index = 1, key, max = len - 1; obj && (key = args[index]); ++index) { 80 | if(index === max) { 81 | obj[key] = null; 82 | delete obj[key]; 83 | } 84 | else { 85 | obj = obj[key]; 86 | } 87 | } 88 | 89 | localStorage[rootname] = JSON.stringify(root); 90 | } 91 | 92 | } 93 | 94 | exports.easierStorage = { 95 | setItem: setItem, 96 | getItem: getItem, 97 | removeItem: removeItem 98 | }; 99 | }(window || exports)); 100 | -------------------------------------------------------------------------------- /client/easierStorage_test.js: -------------------------------------------------------------------------------- 1 | 2 | (function() { 3 | 4 | describe("easierStorage", function() { 5 | it("should exist", function() { 6 | assert(typeof easierStorage !== "undefined", "easierStorage exists"); 7 | }); 8 | 9 | beforeEach(function() { 10 | for(var key in localStorage) { 11 | localStorage.removeItem(key); 12 | } 13 | }); 14 | 15 | 16 | describe("setItem/getItem", function() { 17 | it("setItem must have at least one key and one value", function() { 18 | var err; 19 | try { 20 | easierStorage.setItem("first"); 21 | } catch(e) { 22 | err = e; 23 | } 24 | 25 | assert(err.toString() === "setItem must be called with at least one key and a value"); 26 | }); 27 | 28 | it("setItem setItems value, getItem getItems value", function() { 29 | easierStorage.setItem("first", "value"); 30 | assert(easierStorage.getItem("first") === "value", "correct getItem after setItem"); 31 | }); 32 | 33 | it("getItem with no keys throws an exception", function() { 34 | var err; 35 | try { 36 | easierStorage.getItem() 37 | } catch(e) { 38 | err = e; 39 | } 40 | assert(err); 41 | }); 42 | 43 | it("setItem/getItem with 2 keys", function() { 44 | easierStorage.setItem("first", "second", "some value"); 45 | assert(easierStorage.getItem("first", "second") === "some value", "setItem/getItem with 2 keys"); 46 | }); 47 | 48 | it("getItem of undefined leaf returns undefined", function() { 49 | assert(typeof easierStorage.getItem("unknown") === "undefined"); 50 | assert(typeof easierStorage.getItem("unknown", "unknown") === "undefined"); 51 | }); 52 | }); 53 | 54 | describe("removeItem", function() { 55 | it("removeItem without a key - throw exception", function() { 56 | var err; 57 | try { 58 | easierStorage.removeItem(); 59 | } catch(e) { 60 | err = e; 61 | } 62 | 63 | assert(err.toString() === "removeItem must be called with at least one key"); 64 | 65 | }); 66 | 67 | it("removeItems an unknown item - does not cause problem", function() { 68 | var err; 69 | try { 70 | easierStorage.removeItem("first"); 71 | } catch(e) { 72 | err = e; 73 | } 74 | 75 | assert(typeof err === "undefined"); 76 | }); 77 | 78 | it("removeItems a leaf", function() { 79 | easierStorage.setItem("first", "second", "third", "value"); 80 | easierStorage.removeItem("first", "second", "third"); 81 | assert(typeof easierStorage.getItem("first", "second", "third") === "undefined"); 82 | }); 83 | 84 | it("removeItems a branch", function() { 85 | easierStorage.setItem("first", "second", "third", "value"); 86 | easierStorage.removeItem("first", "second"); 87 | assert(typeof easierStorage.getItem("first", "second") === "undefined"); 88 | assert(typeof easierStorage.getItem("first").second === "undefined"); 89 | }); 90 | }); 91 | }); 92 | }()); 93 | 94 | 95 | -------------------------------------------------------------------------------- /client/mocha/mocha.js: -------------------------------------------------------------------------------- 1 | ;(function(){ 2 | 3 | 4 | // CommonJS require() 5 | 6 | function require(p){ 7 | var path = require.resolve(p) 8 | , mod = require.modules[path]; 9 | if (!mod) throw new Error('failed to require "' + p + '"'); 10 | if (!mod.exports) { 11 | mod.exports = {}; 12 | mod.call(mod.exports, mod, mod.exports, require.relative(path)); 13 | } 14 | return mod.exports; 15 | } 16 | 17 | require.modules = {}; 18 | 19 | require.resolve = function (path){ 20 | var orig = path 21 | , reg = path + '.js' 22 | , index = path + '/index.js'; 23 | return require.modules[reg] && reg 24 | || require.modules[index] && index 25 | || orig; 26 | }; 27 | 28 | require.register = function (path, fn){ 29 | require.modules[path] = fn; 30 | }; 31 | 32 | require.relative = function (parent) { 33 | return function(p){ 34 | if ('.' != p.charAt(0)) return require(p); 35 | 36 | var path = parent.split('/') 37 | , segs = p.split('/'); 38 | path.pop(); 39 | 40 | for (var i = 0; i < segs.length; i++) { 41 | var seg = segs[i]; 42 | if ('..' == seg) path.pop(); 43 | else if ('.' != seg) path.push(seg); 44 | } 45 | 46 | return require(path.join('/')); 47 | }; 48 | }; 49 | 50 | 51 | require.register("browser/debug.js", function(module, exports, require){ 52 | 53 | module.exports = function(type){ 54 | return function(){ 55 | 56 | } 57 | }; 58 | }); // module: browser/debug.js 59 | 60 | require.register("browser/diff.js", function(module, exports, require){ 61 | 62 | }); // module: browser/diff.js 63 | 64 | require.register("browser/events.js", function(module, exports, require){ 65 | 66 | /** 67 | * Module exports. 68 | */ 69 | 70 | exports.EventEmitter = EventEmitter; 71 | 72 | /** 73 | * Check if `obj` is an array. 74 | */ 75 | 76 | function isArray(obj) { 77 | return '[object Array]' == {}.toString.call(obj); 78 | } 79 | 80 | /** 81 | * Event emitter constructor. 82 | * 83 | * @api public. 84 | */ 85 | 86 | function EventEmitter(){}; 87 | 88 | /** 89 | * Adds a listener. 90 | * 91 | * @api public 92 | */ 93 | 94 | EventEmitter.prototype.on = function (name, fn) { 95 | if (!this.$events) { 96 | this.$events = {}; 97 | } 98 | 99 | if (!this.$events[name]) { 100 | this.$events[name] = fn; 101 | } else if (isArray(this.$events[name])) { 102 | this.$events[name].push(fn); 103 | } else { 104 | this.$events[name] = [this.$events[name], fn]; 105 | } 106 | 107 | return this; 108 | }; 109 | 110 | EventEmitter.prototype.addListener = EventEmitter.prototype.on; 111 | 112 | /** 113 | * Adds a volatile listener. 114 | * 115 | * @api public 116 | */ 117 | 118 | EventEmitter.prototype.once = function (name, fn) { 119 | var self = this; 120 | 121 | function on () { 122 | self.removeListener(name, on); 123 | fn.apply(this, arguments); 124 | }; 125 | 126 | on.listener = fn; 127 | this.on(name, on); 128 | 129 | return this; 130 | }; 131 | 132 | /** 133 | * Removes a listener. 134 | * 135 | * @api public 136 | */ 137 | 138 | EventEmitter.prototype.removeListener = function (name, fn) { 139 | if (this.$events && this.$events[name]) { 140 | var list = this.$events[name]; 141 | 142 | if (isArray(list)) { 143 | var pos = -1; 144 | 145 | for (var i = 0, l = list.length; i < l; i++) { 146 | if (list[i] === fn || (list[i].listener && list[i].listener === fn)) { 147 | pos = i; 148 | break; 149 | } 150 | } 151 | 152 | if (pos < 0) { 153 | return this; 154 | } 155 | 156 | list.splice(pos, 1); 157 | 158 | if (!list.length) { 159 | delete this.$events[name]; 160 | } 161 | } else if (list === fn || (list.listener && list.listener === fn)) { 162 | delete this.$events[name]; 163 | } 164 | } 165 | 166 | return this; 167 | }; 168 | 169 | /** 170 | * Removes all listeners for an event. 171 | * 172 | * @api public 173 | */ 174 | 175 | EventEmitter.prototype.removeAllListeners = function (name) { 176 | if (name === undefined) { 177 | this.$events = {}; 178 | return this; 179 | } 180 | 181 | if (this.$events && this.$events[name]) { 182 | this.$events[name] = null; 183 | } 184 | 185 | return this; 186 | }; 187 | 188 | /** 189 | * Gets all listeners for a certain event. 190 | * 191 | * @api publci 192 | */ 193 | 194 | EventEmitter.prototype.listeners = function (name) { 195 | if (!this.$events) { 196 | this.$events = {}; 197 | } 198 | 199 | if (!this.$events[name]) { 200 | this.$events[name] = []; 201 | } 202 | 203 | if (!isArray(this.$events[name])) { 204 | this.$events[name] = [this.$events[name]]; 205 | } 206 | 207 | return this.$events[name]; 208 | }; 209 | 210 | /** 211 | * Emits an event. 212 | * 213 | * @api public 214 | */ 215 | 216 | EventEmitter.prototype.emit = function (name) { 217 | if (!this.$events) { 218 | return false; 219 | } 220 | 221 | var handler = this.$events[name]; 222 | 223 | if (!handler) { 224 | return false; 225 | } 226 | 227 | var args = [].slice.call(arguments, 1); 228 | 229 | if ('function' == typeof handler) { 230 | handler.apply(this, args); 231 | } else if (isArray(handler)) { 232 | var listeners = handler.slice(); 233 | 234 | for (var i = 0, l = listeners.length; i < l; i++) { 235 | listeners[i].apply(this, args); 236 | } 237 | } else { 238 | return false; 239 | } 240 | 241 | return true; 242 | }; 243 | }); // module: browser/events.js 244 | 245 | require.register("browser/fs.js", function(module, exports, require){ 246 | 247 | }); // module: browser/fs.js 248 | 249 | require.register("browser/path.js", function(module, exports, require){ 250 | 251 | }); // module: browser/path.js 252 | 253 | require.register("browser/progress.js", function(module, exports, require){ 254 | 255 | /** 256 | * Expose `Progress`. 257 | */ 258 | 259 | module.exports = Progress; 260 | 261 | /** 262 | * Initialize a new `Progress` indicator. 263 | */ 264 | 265 | function Progress() { 266 | this.percent = 0; 267 | this.size(0); 268 | this.fontSize(11); 269 | this.font('helvetica, arial, sans-serif'); 270 | } 271 | 272 | /** 273 | * Set progress size to `n`. 274 | * 275 | * @param {Number} n 276 | * @return {Progress} for chaining 277 | * @api public 278 | */ 279 | 280 | Progress.prototype.size = function(n){ 281 | this._size = n; 282 | return this; 283 | }; 284 | 285 | /** 286 | * Set text to `str`. 287 | * 288 | * @param {String} str 289 | * @return {Progress} for chaining 290 | * @api public 291 | */ 292 | 293 | Progress.prototype.text = function(str){ 294 | this._text = str; 295 | return this; 296 | }; 297 | 298 | /** 299 | * Set font size to `n`. 300 | * 301 | * @param {Number} n 302 | * @return {Progress} for chaining 303 | * @api public 304 | */ 305 | 306 | Progress.prototype.fontSize = function(n){ 307 | this._fontSize = n; 308 | return this; 309 | }; 310 | 311 | /** 312 | * Set font `family`. 313 | * 314 | * @param {String} family 315 | * @return {Progress} for chaining 316 | */ 317 | 318 | Progress.prototype.font = function(family){ 319 | this._font = family; 320 | return this; 321 | }; 322 | 323 | /** 324 | * Update percentage to `n`. 325 | * 326 | * @param {Number} n 327 | * @return {Progress} for chaining 328 | */ 329 | 330 | Progress.prototype.update = function(n){ 331 | this.percent = n; 332 | return this; 333 | }; 334 | 335 | /** 336 | * Draw on `ctx`. 337 | * 338 | * @param {CanvasRenderingContext2d} ctx 339 | * @return {Progress} for chaining 340 | */ 341 | 342 | Progress.prototype.draw = function(ctx){ 343 | var percent = Math.min(this.percent, 100) 344 | , size = this._size 345 | , half = size / 2 346 | , x = half 347 | , y = half 348 | , rad = half - 1 349 | , fontSize = this._fontSize; 350 | 351 | ctx.font = fontSize + 'px ' + this._font; 352 | 353 | var angle = Math.PI * 2 * (percent / 100); 354 | ctx.clearRect(0, 0, size, size); 355 | 356 | // outer circle 357 | ctx.strokeStyle = '#9f9f9f'; 358 | ctx.beginPath(); 359 | ctx.arc(x, y, rad, 0, angle, false); 360 | ctx.stroke(); 361 | 362 | // inner circle 363 | ctx.strokeStyle = '#eee'; 364 | ctx.beginPath(); 365 | ctx.arc(x, y, rad - 1, 0, angle, true); 366 | ctx.stroke(); 367 | 368 | // text 369 | var text = this._text || (percent | 0) + '%' 370 | , w = ctx.measureText(text).width; 371 | 372 | ctx.fillText( 373 | text 374 | , x - w / 2 + 1 375 | , y + fontSize / 2 - 1); 376 | 377 | return this; 378 | }; 379 | 380 | }); // module: browser/progress.js 381 | 382 | require.register("browser/tty.js", function(module, exports, require){ 383 | 384 | exports.isatty = function(){ 385 | return true; 386 | }; 387 | 388 | exports.getWindowSize = function(){ 389 | return [window.innerHeight, window.innerWidth]; 390 | }; 391 | }); // module: browser/tty.js 392 | 393 | require.register("context.js", function(module, exports, require){ 394 | 395 | /** 396 | * Expose `Context`. 397 | */ 398 | 399 | module.exports = Context; 400 | 401 | /** 402 | * Initialize a new `Context`. 403 | * 404 | * @api private 405 | */ 406 | 407 | function Context(){} 408 | 409 | /** 410 | * Set the context `Runnable` to `runnable`. 411 | * 412 | * @param {Runnable} runnable 413 | * @return {Context} 414 | * @api private 415 | */ 416 | 417 | Context.prototype.runnable = function(runnable){ 418 | this._runnable = runnable; 419 | return this; 420 | }; 421 | 422 | /** 423 | * Set test timeout `ms`. 424 | * 425 | * @param {Number} ms 426 | * @return {Context} self 427 | * @api private 428 | */ 429 | 430 | Context.prototype.timeout = function(ms){ 431 | this._runnable.timeout(ms); 432 | return this; 433 | }; 434 | 435 | /** 436 | * Inspect the context void of `._runnable`. 437 | * 438 | * @return {String} 439 | * @api private 440 | */ 441 | 442 | Context.prototype.inspect = function(){ 443 | return JSON.stringify(this, function(key, val){ 444 | return '_runnable' == key 445 | ? undefined 446 | : val; 447 | }, 2); 448 | }; 449 | 450 | }); // module: context.js 451 | 452 | require.register("hook.js", function(module, exports, require){ 453 | 454 | /** 455 | * Module dependencies. 456 | */ 457 | 458 | var Runnable = require('./runnable'); 459 | 460 | /** 461 | * Expose `Hook`. 462 | */ 463 | 464 | module.exports = Hook; 465 | 466 | /** 467 | * Initialize a new `Hook` with the given `title` and callback `fn`. 468 | * 469 | * @param {String} title 470 | * @param {Function} fn 471 | * @api private 472 | */ 473 | 474 | function Hook(title, fn) { 475 | Runnable.call(this, title, fn); 476 | this.type = 'hook'; 477 | } 478 | 479 | /** 480 | * Inherit from `Runnable.prototype`. 481 | */ 482 | 483 | Hook.prototype = new Runnable; 484 | Hook.prototype.constructor = Hook; 485 | 486 | 487 | }); // module: hook.js 488 | 489 | require.register("interfaces/bdd.js", function(module, exports, require){ 490 | 491 | /** 492 | * Module dependencies. 493 | */ 494 | 495 | var Suite = require('../suite') 496 | , Test = require('../test'); 497 | 498 | /** 499 | * BDD-style interface: 500 | * 501 | * describe('Array', function(){ 502 | * describe('#indexOf()', function(){ 503 | * it('should return -1 when not present', function(){ 504 | * 505 | * }); 506 | * 507 | * it('should return the index when present', function(){ 508 | * 509 | * }); 510 | * }); 511 | * }); 512 | * 513 | */ 514 | 515 | module.exports = function(suite){ 516 | var suites = [suite]; 517 | 518 | suite.on('pre-require', function(context){ 519 | 520 | // noop variants 521 | 522 | context.xdescribe = function(){}; 523 | context.xit = function(){}; 524 | 525 | /** 526 | * Execute before running tests. 527 | */ 528 | 529 | context.before = function(fn){ 530 | suites[0].beforeAll(fn); 531 | }; 532 | 533 | /** 534 | * Execute after running tests. 535 | */ 536 | 537 | context.after = function(fn){ 538 | suites[0].afterAll(fn); 539 | }; 540 | 541 | /** 542 | * Execute before each test case. 543 | */ 544 | 545 | context.beforeEach = function(fn){ 546 | suites[0].beforeEach(fn); 547 | }; 548 | 549 | /** 550 | * Execute after each test case. 551 | */ 552 | 553 | context.afterEach = function(fn){ 554 | suites[0].afterEach(fn); 555 | }; 556 | 557 | /** 558 | * Describe a "suite" with the given `title` 559 | * and callback `fn` containing nested suites 560 | * and/or tests. 561 | */ 562 | 563 | context.describe = function(title, fn){ 564 | var suite = Suite.create(suites[0], title); 565 | suites.unshift(suite); 566 | fn(); 567 | suites.shift(); 568 | }; 569 | 570 | /** 571 | * Describe a specification or test-case 572 | * with the given `title` and callback `fn` 573 | * acting as a thunk. 574 | */ 575 | 576 | context.it = function(title, fn){ 577 | suites[0].addTest(new Test(title, fn)); 578 | }; 579 | }); 580 | }; 581 | 582 | }); // module: interfaces/bdd.js 583 | 584 | require.register("interfaces/exports.js", function(module, exports, require){ 585 | 586 | /** 587 | * Module dependencies. 588 | */ 589 | 590 | var Suite = require('../suite') 591 | , Test = require('../test'); 592 | 593 | /** 594 | * TDD-style interface: 595 | * 596 | * exports.Array = { 597 | * '#indexOf()': { 598 | * 'should return -1 when the value is not present': function(){ 599 | * 600 | * }, 601 | * 602 | * 'should return the correct index when the value is present': function(){ 603 | * 604 | * } 605 | * } 606 | * }; 607 | * 608 | */ 609 | 610 | module.exports = function(suite){ 611 | var suites = [suite]; 612 | 613 | suite.on('require', visit); 614 | 615 | function visit(obj) { 616 | var suite; 617 | for (var key in obj) { 618 | if ('function' == typeof obj[key]) { 619 | var fn = obj[key]; 620 | switch (key) { 621 | case 'before': 622 | suites[0].beforeAll(fn); 623 | break; 624 | case 'after': 625 | suites[0].afterAll(fn); 626 | break; 627 | case 'beforeEach': 628 | suites[0].beforeEach(fn); 629 | break; 630 | case 'afterEach': 631 | suites[0].afterEach(fn); 632 | break; 633 | default: 634 | suites[0].addTest(new Test(key, fn)); 635 | } 636 | } else { 637 | var suite = Suite.create(suites[0], key); 638 | suites.unshift(suite); 639 | visit(obj[key]); 640 | suites.shift(); 641 | } 642 | } 643 | } 644 | }; 645 | }); // module: interfaces/exports.js 646 | 647 | require.register("interfaces/index.js", function(module, exports, require){ 648 | 649 | exports.bdd = require('./bdd'); 650 | exports.tdd = require('./tdd'); 651 | exports.qunit = require('./qunit'); 652 | exports.exports = require('./exports'); 653 | 654 | }); // module: interfaces/index.js 655 | 656 | require.register("interfaces/qunit.js", function(module, exports, require){ 657 | 658 | /** 659 | * Module dependencies. 660 | */ 661 | 662 | var Suite = require('../suite') 663 | , Test = require('../test'); 664 | 665 | /** 666 | * QUnit-style interface: 667 | * 668 | * suite('Array'); 669 | * 670 | * test('#length', function(){ 671 | * var arr = [1,2,3]; 672 | * ok(arr.length == 3); 673 | * }); 674 | * 675 | * test('#indexOf()', function(){ 676 | * var arr = [1,2,3]; 677 | * ok(arr.indexOf(1) == 0); 678 | * ok(arr.indexOf(2) == 1); 679 | * ok(arr.indexOf(3) == 2); 680 | * }); 681 | * 682 | * suite('String'); 683 | * 684 | * test('#length', function(){ 685 | * ok('foo'.length == 3); 686 | * }); 687 | * 688 | */ 689 | 690 | module.exports = function(suite){ 691 | var suites = [suite]; 692 | 693 | suite.on('pre-require', function(context){ 694 | 695 | /** 696 | * Execute before running tests. 697 | */ 698 | 699 | context.before = function(fn){ 700 | suites[0].beforeAll(fn); 701 | }; 702 | 703 | /** 704 | * Execute after running tests. 705 | */ 706 | 707 | context.after = function(fn){ 708 | suites[0].afterAll(fn); 709 | }; 710 | 711 | /** 712 | * Execute before each test case. 713 | */ 714 | 715 | context.beforeEach = function(fn){ 716 | suites[0].beforeEach(fn); 717 | }; 718 | 719 | /** 720 | * Execute after each test case. 721 | */ 722 | 723 | context.afterEach = function(fn){ 724 | suites[0].afterEach(fn); 725 | }; 726 | 727 | /** 728 | * Describe a "suite" with the given `title`. 729 | */ 730 | 731 | context.suite = function(title){ 732 | if (suites.length > 1) suites.shift(); 733 | var suite = Suite.create(suites[0], title); 734 | suites.unshift(suite); 735 | }; 736 | 737 | /** 738 | * Describe a specification or test-case 739 | * with the given `title` and callback `fn` 740 | * acting as a thunk. 741 | */ 742 | 743 | context.test = function(title, fn){ 744 | suites[0].addTest(new Test(title, fn)); 745 | }; 746 | }); 747 | }; 748 | 749 | }); // module: interfaces/qunit.js 750 | 751 | require.register("interfaces/tdd.js", function(module, exports, require){ 752 | 753 | /** 754 | * Module dependencies. 755 | */ 756 | 757 | var Suite = require('../suite') 758 | , Test = require('../test'); 759 | 760 | /** 761 | * TDD-style interface: 762 | * 763 | * suite('Array', function(){ 764 | * suite('#indexOf()', function(){ 765 | * suiteSetup(function(){ 766 | * 767 | * }); 768 | * 769 | * test('should return -1 when not present', function(){ 770 | * 771 | * }); 772 | * 773 | * test('should return the index when present', function(){ 774 | * 775 | * }); 776 | * 777 | * suiteTeardown(function(){ 778 | * 779 | * }); 780 | * }); 781 | * }); 782 | * 783 | */ 784 | 785 | module.exports = function(suite){ 786 | var suites = [suite]; 787 | 788 | suite.on('pre-require', function(context){ 789 | 790 | /** 791 | * Execute before each test case. 792 | */ 793 | 794 | context.setup = function(fn){ 795 | suites[0].beforeEach(fn); 796 | }; 797 | 798 | /** 799 | * Execute after each test case. 800 | */ 801 | 802 | context.teardown = function(fn){ 803 | suites[0].afterEach(fn); 804 | }; 805 | 806 | /** 807 | * Execute before the suite. 808 | */ 809 | 810 | context.suiteSetup = function(fn){ 811 | suites[0].beforeAll(fn); 812 | }; 813 | 814 | /** 815 | * Execute after the suite. 816 | */ 817 | 818 | context.suiteTeardown = function(fn){ 819 | suites[0].afterAll(fn); 820 | }; 821 | 822 | /** 823 | * Describe a "suite" with the given `title` 824 | * and callback `fn` containing nested suites 825 | * and/or tests. 826 | */ 827 | 828 | context.suite = function(title, fn){ 829 | var suite = Suite.create(suites[0], title); 830 | suites.unshift(suite); 831 | fn(); 832 | suites.shift(); 833 | }; 834 | 835 | /** 836 | * Describe a specification or test-case 837 | * with the given `title` and callback `fn` 838 | * acting as a thunk. 839 | */ 840 | 841 | context.test = function(title, fn){ 842 | suites[0].addTest(new Test(title, fn)); 843 | }; 844 | }); 845 | }; 846 | 847 | }); // module: interfaces/tdd.js 848 | 849 | require.register("mocha.js", function(module, exports, require){ 850 | 851 | /*! 852 | * mocha 853 | * Copyright(c) 2011 TJ Holowaychuk 854 | * MIT Licensed 855 | */ 856 | 857 | /** 858 | * Module dependencies. 859 | */ 860 | 861 | var path = require('browser/path'); 862 | 863 | /** 864 | * Expose `Mocha`. 865 | */ 866 | 867 | exports = module.exports = Mocha; 868 | 869 | /** 870 | * Library version. 871 | */ 872 | 873 | exports.version = '1.0.1'; 874 | 875 | /** 876 | * Expose internals. 877 | */ 878 | 879 | exports.utils = require('./utils'); 880 | exports.interfaces = require('./interfaces'); 881 | exports.reporters = require('./reporters'); 882 | exports.Runnable = require('./runnable'); 883 | exports.Context = require('./context'); 884 | exports.Runner = require('./runner'); 885 | exports.Suite = require('./suite'); 886 | exports.Hook = require('./hook'); 887 | exports.Test = require('./test'); 888 | 889 | /** 890 | * Return image `name` path. 891 | * 892 | * @param {String} name 893 | * @return {String} 894 | * @api private 895 | */ 896 | 897 | function image(name) { 898 | return __dirname + '/../images/' + name + '.png'; 899 | } 900 | 901 | /** 902 | * Setup mocha with `options`. 903 | * 904 | * Options: 905 | * 906 | * - `ui` name "bdd", "tdd", "exports" etc 907 | * - `reporter` reporter instance, defaults to `mocha.reporters.Dot` 908 | * - `globals` array of accepted globals 909 | * - `timeout` timeout in milliseconds 910 | * - `ignoreLeaks` ignore global leaks 911 | * 912 | * @param {Object} options 913 | * @api public 914 | */ 915 | 916 | function Mocha(options) { 917 | options = options || {}; 918 | this.files = []; 919 | this.options = options; 920 | this.suite = new exports.Suite('', new exports.Context); 921 | this.ui(options.ui); 922 | this.reporter(options.reporter); 923 | if (options.timeout) this.suite.timeout(options.timeout); 924 | } 925 | 926 | /** 927 | * Add test `file`. 928 | * 929 | * @param {String} file 930 | * @api public 931 | */ 932 | 933 | Mocha.prototype.addFile = function(file){ 934 | this.files.push(file); 935 | return this; 936 | }; 937 | 938 | /** 939 | * Set reporter to `name`, defaults to "dot". 940 | * 941 | * @param {String} name 942 | * @api public 943 | */ 944 | 945 | Mocha.prototype.reporter = function(name){ 946 | name = name || 'dot'; 947 | this._reporter = require('./reporters/' + name); 948 | if (!this._reporter) throw new Error('invalid reporter "' + name + '"'); 949 | return this; 950 | }; 951 | 952 | /** 953 | * Set test UI `name`, defaults to "bdd". 954 | * 955 | * @param {String} bdd 956 | * @api public 957 | */ 958 | 959 | Mocha.prototype.ui = function(name){ 960 | name = name || 'bdd'; 961 | this._ui = exports.interfaces[name]; 962 | if (!this._ui) throw new Error('invalid interface "' + name + '"'); 963 | this._ui = this._ui(this.suite); 964 | return this; 965 | }; 966 | 967 | /** 968 | * Load registered files. 969 | * 970 | * @api private 971 | */ 972 | 973 | Mocha.prototype.loadFiles = function(){ 974 | var suite = this.suite; 975 | this.files.forEach(function(file){ 976 | file = path.resolve(file); 977 | suite.emit('pre-require', global, file); 978 | suite.emit('require', require(file), file); 979 | suite.emit('post-require', global, file); 980 | }); 981 | }; 982 | 983 | /** 984 | * Enable growl support. 985 | * 986 | * @api private 987 | */ 988 | 989 | Mocha.prototype.growl = function(runner, reporter) { 990 | var notify = require('growl'); 991 | 992 | runner.on('end', function(){ 993 | var stats = reporter.stats; 994 | if (stats.failures) { 995 | var msg = stats.failures + ' of ' + runner.total + ' tests failed'; 996 | notify(msg, { title: 'Failed', image: image('fail') }); 997 | } else { 998 | notify(stats.passes + ' tests passed in ' + stats.duration + 'ms', { 999 | title: 'Passed' 1000 | , image: image('pass') 1001 | }); 1002 | } 1003 | }); 1004 | }; 1005 | 1006 | /** 1007 | * Run tests and invoke `fn()` when complete. 1008 | * 1009 | * @param {Function} fn 1010 | * @return {Runner} 1011 | * @api public 1012 | */ 1013 | 1014 | Mocha.prototype.run = function(fn){ 1015 | this.loadFiles(); 1016 | var suite = this.suite; 1017 | var options = this.options; 1018 | var runner = new exports.Runner(suite); 1019 | var reporter = new this._reporter(runner); 1020 | runner.ignoreLeaks = options.ignoreLeaks; 1021 | if (options.grep) runner.grep(options.grep); 1022 | if (options.globals) runner.globals(options.globals); 1023 | if (options.growl) this.growl(runner, reporter); 1024 | return runner.run(fn); 1025 | }; 1026 | 1027 | }); // module: mocha.js 1028 | 1029 | require.register("reporters/base.js", function(module, exports, require){ 1030 | 1031 | /** 1032 | * Module dependencies. 1033 | */ 1034 | 1035 | var tty = require('browser/tty') 1036 | , diff = require('browser/diff'); 1037 | 1038 | /** 1039 | * Check if both stdio streams are associated with a tty. 1040 | */ 1041 | 1042 | var isatty = tty.isatty(1) && tty.isatty(2); 1043 | 1044 | /** 1045 | * Expose `Base`. 1046 | */ 1047 | 1048 | exports = module.exports = Base; 1049 | 1050 | /** 1051 | * Enable coloring by default. 1052 | */ 1053 | 1054 | exports.useColors = isatty; 1055 | 1056 | /** 1057 | * Default color map. 1058 | */ 1059 | 1060 | exports.colors = { 1061 | 'pass': 90 1062 | , 'fail': 31 1063 | , 'bright pass': 92 1064 | , 'bright fail': 91 1065 | , 'bright yellow': 93 1066 | , 'pending': 36 1067 | , 'suite': 0 1068 | , 'error title': 0 1069 | , 'error message': 31 1070 | , 'error stack': 90 1071 | , 'checkmark': 32 1072 | , 'fast': 90 1073 | , 'medium': 33 1074 | , 'slow': 31 1075 | , 'green': 32 1076 | , 'light': 90 1077 | , 'diff gutter': 90 1078 | , 'diff added': 42 1079 | , 'diff removed': 41 1080 | }; 1081 | 1082 | /** 1083 | * Color `str` with the given `type`, 1084 | * allowing colors to be disabled, 1085 | * as well as user-defined color 1086 | * schemes. 1087 | * 1088 | * @param {String} type 1089 | * @param {String} str 1090 | * @return {String} 1091 | * @api private 1092 | */ 1093 | 1094 | var color = exports.color = function(type, str) { 1095 | if (!exports.useColors) return str; 1096 | return '\033[' + exports.colors[type] + 'm' + str + '\033[0m'; 1097 | }; 1098 | 1099 | /** 1100 | * Expose term window size, with some 1101 | * defaults for when stderr is not a tty. 1102 | */ 1103 | 1104 | exports.window = { 1105 | width: isatty 1106 | ? process.stdout.getWindowSize 1107 | ? process.stdout.getWindowSize(1)[0] 1108 | : tty.getWindowSize()[1] 1109 | : 75 1110 | }; 1111 | 1112 | /** 1113 | * Expose some basic cursor interactions 1114 | * that are common among reporters. 1115 | */ 1116 | 1117 | exports.cursor = { 1118 | hide: function(){ 1119 | process.stdout.write('\033[?25l'); 1120 | }, 1121 | 1122 | show: function(){ 1123 | process.stdout.write('\033[?25h'); 1124 | }, 1125 | 1126 | deleteLine: function(){ 1127 | process.stdout.write('\033[2K'); 1128 | }, 1129 | 1130 | beginningOfLine: function(){ 1131 | process.stdout.write('\033[0G'); 1132 | }, 1133 | 1134 | CR: function(){ 1135 | exports.cursor.deleteLine(); 1136 | exports.cursor.beginningOfLine(); 1137 | } 1138 | }; 1139 | 1140 | /** 1141 | * A test is considered slow if it 1142 | * exceeds the following value in milliseconds. 1143 | */ 1144 | 1145 | exports.slow = 75; 1146 | 1147 | /** 1148 | * Outut the given `failures` as a list. 1149 | * 1150 | * @param {Array} failures 1151 | * @api public 1152 | */ 1153 | 1154 | exports.list = function(failures){ 1155 | console.error(); 1156 | failures.forEach(function(test, i){ 1157 | // format 1158 | var fmt = color('error title', ' %s) %s:\n') 1159 | + color('error message', ' %s') 1160 | + color('error stack', '\n%s\n'); 1161 | 1162 | // msg 1163 | var err = test.err 1164 | , message = err.message || '' 1165 | , stack = err.stack || message 1166 | , index = stack.indexOf(message) + message.length 1167 | , msg = stack.slice(0, index) 1168 | , actual = err.actual 1169 | , expected = err.expected; 1170 | 1171 | // actual / expected diff 1172 | if ('string' == typeof actual && 'string' == typeof expected) { 1173 | var len = Math.max(actual.length, expected.length); 1174 | 1175 | if (len < 20) msg = errorDiff(err, 'Chars'); 1176 | else msg = errorDiff(err, 'Words'); 1177 | 1178 | // linenos 1179 | var lines = msg.split('\n'); 1180 | if (lines.length > 4) { 1181 | var width = String(lines.length).length; 1182 | msg = lines.map(function(str, i){ 1183 | return pad(++i, width) + ' |' + ' ' + str; 1184 | }).join('\n'); 1185 | } 1186 | 1187 | // legend 1188 | msg = '\n' 1189 | + color('diff removed', 'actual') 1190 | + ' ' 1191 | + color('diff added', 'expected') 1192 | + '\n\n' 1193 | + msg 1194 | + '\n'; 1195 | 1196 | // indent 1197 | msg = msg.replace(/^/gm, ' '); 1198 | 1199 | fmt = color('error title', ' %s) %s:\n%s') 1200 | + color('error stack', '\n%s\n'); 1201 | } 1202 | 1203 | // indent stack trace without msg 1204 | stack = stack.slice(index ? index + 1 : index) 1205 | .replace(/^/gm, ' '); 1206 | 1207 | console.error(fmt, (i + 1), test.fullTitle(), msg, stack); 1208 | }); 1209 | }; 1210 | 1211 | /** 1212 | * Initialize a new `Base` reporter. 1213 | * 1214 | * All other reporters generally 1215 | * inherit from this reporter, providing 1216 | * stats such as test duration, number 1217 | * of tests passed / failed etc. 1218 | * 1219 | * @param {Runner} runner 1220 | * @api public 1221 | */ 1222 | 1223 | function Base(runner) { 1224 | var self = this 1225 | , stats = this.stats = { suites: 0, tests: 0, passes: 0, failures: 0 } 1226 | , failures = this.failures = []; 1227 | 1228 | if (!runner) return; 1229 | this.runner = runner; 1230 | 1231 | runner.on('start', function(){ 1232 | stats.start = new Date; 1233 | }); 1234 | 1235 | runner.on('suite', function(suite){ 1236 | stats.suites = stats.suites || 0; 1237 | suite.root || stats.suites++; 1238 | }); 1239 | 1240 | runner.on('test end', function(test){ 1241 | stats.tests = stats.tests || 0; 1242 | stats.tests++; 1243 | }); 1244 | 1245 | runner.on('pass', function(test){ 1246 | stats.passes = stats.passes || 0; 1247 | 1248 | var medium = exports.slow / 2; 1249 | test.speed = test.duration > exports.slow 1250 | ? 'slow' 1251 | : test.duration > medium 1252 | ? 'medium' 1253 | : 'fast'; 1254 | 1255 | stats.passes++; 1256 | }); 1257 | 1258 | runner.on('fail', function(test, err){ 1259 | stats.failures = stats.failures || 0; 1260 | stats.failures++; 1261 | test.err = err; 1262 | failures.push(test); 1263 | }); 1264 | 1265 | runner.on('end', function(){ 1266 | stats.end = new Date; 1267 | stats.duration = new Date - stats.start; 1268 | }); 1269 | } 1270 | 1271 | /** 1272 | * Output common epilogue used by many of 1273 | * the bundled reporters. 1274 | * 1275 | * @api public 1276 | */ 1277 | 1278 | Base.prototype.epilogue = function(){ 1279 | var stats = this.stats 1280 | , fmt; 1281 | 1282 | console.log(); 1283 | 1284 | // failure 1285 | if (stats.failures) { 1286 | fmt = color('bright fail', ' ✖') 1287 | + color('fail', ' %d of %d tests failed') 1288 | + color('light', ':') 1289 | 1290 | console.error(fmt, stats.failures, this.runner.total); 1291 | Base.list(this.failures); 1292 | console.error(); 1293 | return; 1294 | } 1295 | 1296 | // pass 1297 | fmt = color('bright pass', ' ✔') 1298 | + color('green', ' %d tests complete') 1299 | + color('light', ' (%dms)'); 1300 | 1301 | console.log(fmt, stats.tests || 0, stats.duration); 1302 | console.log(); 1303 | }; 1304 | 1305 | /** 1306 | * Pad the given `str` to `len`. 1307 | * 1308 | * @param {String} str 1309 | * @param {String} len 1310 | * @return {String} 1311 | * @api private 1312 | */ 1313 | 1314 | function pad(str, len) { 1315 | str = String(str); 1316 | return Array(len - str.length + 1).join(' ') + str; 1317 | } 1318 | 1319 | /** 1320 | * Return a character diff for `err`. 1321 | * 1322 | * @param {Error} err 1323 | * @return {String} 1324 | * @api private 1325 | */ 1326 | 1327 | function errorDiff(err, type) { 1328 | return diff['diff' + type](err.actual, err.expected).map(function(str){ 1329 | if (str.added) return colorLines('diff added', str.value); 1330 | if (str.removed) return colorLines('diff removed', str.value); 1331 | return str.value; 1332 | }).join(''); 1333 | } 1334 | 1335 | /** 1336 | * Color lines for `str`, using the color `name`. 1337 | * 1338 | * @param {String} name 1339 | * @param {String} str 1340 | * @return {String} 1341 | * @api private 1342 | */ 1343 | 1344 | function colorLines(name, str) { 1345 | return str.split('\n').map(function(str){ 1346 | return color(name, str); 1347 | }).join('\n'); 1348 | } 1349 | 1350 | }); // module: reporters/base.js 1351 | 1352 | require.register("reporters/doc.js", function(module, exports, require){ 1353 | 1354 | /** 1355 | * Module dependencies. 1356 | */ 1357 | 1358 | var Base = require('./base') 1359 | , utils = require('../utils'); 1360 | 1361 | /** 1362 | * Expose `Doc`. 1363 | */ 1364 | 1365 | exports = module.exports = Doc; 1366 | 1367 | /** 1368 | * Initialize a new `Doc` reporter. 1369 | * 1370 | * @param {Runner} runner 1371 | * @api public 1372 | */ 1373 | 1374 | function Doc(runner) { 1375 | Base.call(this, runner); 1376 | 1377 | var self = this 1378 | , stats = this.stats 1379 | , total = runner.total 1380 | , indents = 2; 1381 | 1382 | function indent() { 1383 | return Array(indents).join(' '); 1384 | } 1385 | 1386 | runner.on('suite', function(suite){ 1387 | if (suite.root) return; 1388 | ++indents; 1389 | console.log('%s
', indent()); 1390 | ++indents; 1391 | console.log('%s

%s

', indent(), suite.title); 1392 | console.log('%s
', indent()); 1393 | }); 1394 | 1395 | runner.on('suite end', function(suite){ 1396 | if (suite.root) return; 1397 | console.log('%s
', indent()); 1398 | --indents; 1399 | console.log('%s
', indent()); 1400 | --indents; 1401 | }); 1402 | 1403 | runner.on('pass', function(test){ 1404 | console.log('%s
%s
', indent(), test.title); 1405 | var code = utils.escape(clean(test.fn.toString())); 1406 | console.log('%s
%s
', indent(), code); 1407 | }); 1408 | } 1409 | 1410 | /** 1411 | * Strip the function definition from `str`, 1412 | * and re-indent for pre whitespace. 1413 | */ 1414 | 1415 | function clean(str) { 1416 | str = str 1417 | .replace(/^function *\(.*\) *{/, '') 1418 | .replace(/\s+\}$/, ''); 1419 | 1420 | var spaces = str.match(/^\n?( *)/)[1].length 1421 | , re = new RegExp('^ {' + spaces + '}', 'gm'); 1422 | 1423 | str = str.replace(re, ''); 1424 | 1425 | return str; 1426 | } 1427 | }); // module: reporters/doc.js 1428 | 1429 | require.register("reporters/dot.js", function(module, exports, require){ 1430 | 1431 | /** 1432 | * Module dependencies. 1433 | */ 1434 | 1435 | var Base = require('./base') 1436 | , color = Base.color; 1437 | 1438 | /** 1439 | * Expose `Dot`. 1440 | */ 1441 | 1442 | exports = module.exports = Dot; 1443 | 1444 | /** 1445 | * Initialize a new `Dot` matrix test reporter. 1446 | * 1447 | * @param {Runner} runner 1448 | * @api public 1449 | */ 1450 | 1451 | function Dot(runner) { 1452 | Base.call(this, runner); 1453 | 1454 | var self = this 1455 | , stats = this.stats 1456 | , width = Base.window.width * .75 | 0 1457 | , n = 0; 1458 | 1459 | runner.on('start', function(){ 1460 | process.stdout.write('\n '); 1461 | }); 1462 | 1463 | runner.on('pending', function(test){ 1464 | process.stdout.write(color('pending', '.')); 1465 | }); 1466 | 1467 | runner.on('pass', function(test){ 1468 | if (++n % width == 0) process.stdout.write('\n '); 1469 | if ('slow' == test.speed) { 1470 | process.stdout.write(color('bright yellow', '.')); 1471 | } else { 1472 | process.stdout.write(color(test.speed, '.')); 1473 | } 1474 | }); 1475 | 1476 | runner.on('fail', function(test, err){ 1477 | if (++n % width == 0) process.stdout.write('\n '); 1478 | process.stdout.write(color('fail', '.')); 1479 | }); 1480 | 1481 | runner.on('end', function(){ 1482 | console.log(); 1483 | self.epilogue(); 1484 | }); 1485 | } 1486 | 1487 | /** 1488 | * Inherit from `Base.prototype`. 1489 | */ 1490 | 1491 | Dot.prototype = new Base; 1492 | Dot.prototype.constructor = Dot; 1493 | 1494 | }); // module: reporters/dot.js 1495 | 1496 | require.register("reporters/html-cov.js", function(module, exports, require){ 1497 | 1498 | /** 1499 | * Module dependencies. 1500 | */ 1501 | 1502 | var JSONCov = require('./json-cov') 1503 | , fs = require('browser/fs'); 1504 | 1505 | /** 1506 | * Expose `HTMLCov`. 1507 | */ 1508 | 1509 | exports = module.exports = HTMLCov; 1510 | 1511 | /** 1512 | * Initialize a new `JsCoverage` reporter. 1513 | * 1514 | * @param {Runner} runner 1515 | * @api public 1516 | */ 1517 | 1518 | function HTMLCov(runner) { 1519 | var jade = require('jade') 1520 | , file = __dirname + '/templates/coverage.jade' 1521 | , str = fs.readFileSync(file, 'utf8') 1522 | , fn = jade.compile(str, { filename: file }) 1523 | , self = this; 1524 | 1525 | JSONCov.call(this, runner, false); 1526 | 1527 | runner.on('end', function(){ 1528 | process.stdout.write(fn({ 1529 | cov: self.cov 1530 | , coverageClass: coverageClass 1531 | })); 1532 | }); 1533 | } 1534 | 1535 | function coverageClass(n) { 1536 | if (n >= 75) return 'high'; 1537 | if (n >= 50) return 'medium'; 1538 | if (n >= 25) return 'low'; 1539 | return 'terrible'; 1540 | } 1541 | }); // module: reporters/html-cov.js 1542 | 1543 | require.register("reporters/html.js", function(module, exports, require){ 1544 | 1545 | /** 1546 | * Module dependencies. 1547 | */ 1548 | 1549 | var Base = require('./base') 1550 | , utils = require('../utils') 1551 | , Progress = require('../browser/progress') 1552 | , escape = utils.escape; 1553 | 1554 | /** 1555 | * Expose `Doc`. 1556 | */ 1557 | 1558 | exports = module.exports = HTML; 1559 | 1560 | /** 1561 | * Stats template. 1562 | */ 1563 | 1564 | var statsTemplate = ''; 1570 | 1571 | /** 1572 | * Initialize a new `Doc` reporter. 1573 | * 1574 | * @param {Runner} runner 1575 | * @api public 1576 | */ 1577 | 1578 | function HTML(runner) { 1579 | Base.call(this, runner); 1580 | 1581 | var self = this 1582 | , stats = this.stats 1583 | , total = runner.total 1584 | , root = document.getElementById('mocha') 1585 | , stat = fragment(statsTemplate) 1586 | , items = stat.getElementsByTagName('li') 1587 | , passes = items[1].getElementsByTagName('em')[0] 1588 | , failures = items[2].getElementsByTagName('em')[0] 1589 | , duration = items[3].getElementsByTagName('em')[0] 1590 | , canvas = stat.getElementsByTagName('canvas')[0] 1591 | , stack = [root] 1592 | , progress 1593 | , ctx 1594 | 1595 | if (canvas.getContext) { 1596 | ctx = canvas.getContext('2d'); 1597 | progress = new Progress; 1598 | } 1599 | 1600 | if (!root) return error('#mocha div missing, add it to your document'); 1601 | 1602 | root.appendChild(stat); 1603 | 1604 | if (progress) progress.size(40); 1605 | 1606 | runner.on('suite', function(suite){ 1607 | if (suite.root) return; 1608 | 1609 | // suite 1610 | var el = fragment('

%s

', suite.title); 1611 | 1612 | // container 1613 | stack[0].appendChild(el); 1614 | stack.unshift(document.createElement('div')); 1615 | el.appendChild(stack[0]); 1616 | }); 1617 | 1618 | runner.on('suite end', function(suite){ 1619 | if (suite.root) return; 1620 | stack.shift(); 1621 | }); 1622 | 1623 | runner.on('fail', function(test, err){ 1624 | if ('hook' == test.type || err.uncaught) runner.emit('test end', test); 1625 | }); 1626 | 1627 | runner.on('test end', function(test){ 1628 | // TODO: add to stats 1629 | var percent = stats.tests / total * 100 | 0; 1630 | if (progress) progress.update(percent).draw(ctx); 1631 | 1632 | // update stats 1633 | var ms = new Date - stats.start; 1634 | text(passes, stats.passes); 1635 | text(failures, stats.failures); 1636 | text(duration, (ms / 1000).toFixed(2)); 1637 | 1638 | // test 1639 | if ('passed' == test.state) { 1640 | var el = fragment('

%e

', test.title); 1641 | } else if (test.pending) { 1642 | var el = fragment('

%e

', test.title); 1643 | } else { 1644 | var el = fragment('

%e

', test.title); 1645 | var str = test.err.stack || test.err.toString(); 1646 | 1647 | // FF / Opera do not add the message 1648 | if (!~str.indexOf(test.err.message)) { 1649 | str = test.err.message + '\n' + str; 1650 | } 1651 | 1652 | // <=IE7 stringifies to [Object Error]. Since it can be overloaded, we 1653 | // check for the result of the stringifying. 1654 | if ('[object Error]' == str) str = test.err.message; 1655 | 1656 | // Safari doesn't give you a stack. Let's at least provide a source line. 1657 | if (!test.err.stack && test.err.sourceURL && test.err.line !== undefined) { 1658 | str += "\n(" + test.err.sourceURL + ":" + test.err.line + ")"; 1659 | } 1660 | 1661 | el.appendChild(fragment('
%e
', str)); 1662 | } 1663 | 1664 | // toggle code 1665 | var h2 = el.getElementsByTagName('h2')[0]; 1666 | 1667 | on(h2, 'click', function(){ 1668 | pre.style.display = 'none' == pre.style.display 1669 | ? 'block' 1670 | : 'none'; 1671 | }); 1672 | 1673 | // code 1674 | // TODO: defer 1675 | if (!test.pending) { 1676 | var pre = fragment('
%e
', clean(test.fn.toString())); 1677 | el.appendChild(pre); 1678 | pre.style.display = 'none'; 1679 | } 1680 | 1681 | stack[0].appendChild(el); 1682 | }); 1683 | } 1684 | 1685 | /** 1686 | * Display error `msg`. 1687 | */ 1688 | 1689 | function error(msg) { 1690 | document.body.appendChild(fragment('
%s
', msg)); 1691 | } 1692 | 1693 | /** 1694 | * Return a DOM fragment from `html`. 1695 | */ 1696 | 1697 | function fragment(html) { 1698 | var args = arguments 1699 | , div = document.createElement('div') 1700 | , i = 1; 1701 | 1702 | div.innerHTML = html.replace(/%([se])/g, function(_, type){ 1703 | switch (type) { 1704 | case 's': return String(args[i++]); 1705 | case 'e': return escape(args[i++]); 1706 | } 1707 | }); 1708 | 1709 | return div.firstChild; 1710 | } 1711 | 1712 | /** 1713 | * Set `el` text to `str`. 1714 | */ 1715 | 1716 | function text(el, str) { 1717 | if (el.textContent) { 1718 | el.textContent = str; 1719 | } else { 1720 | el.innerText = str; 1721 | } 1722 | } 1723 | 1724 | /** 1725 | * Listen on `event` with callback `fn`. 1726 | */ 1727 | 1728 | function on(el, event, fn) { 1729 | if (el.addEventListener) { 1730 | el.addEventListener(event, fn, false); 1731 | } else { 1732 | el.attachEvent('on' + event, fn); 1733 | } 1734 | } 1735 | 1736 | /** 1737 | * Strip the function definition from `str`, 1738 | * and re-indent for pre whitespace. 1739 | */ 1740 | 1741 | function clean(str) { 1742 | str = str 1743 | .replace(/^function *\(.*\) *{/, '') 1744 | .replace(/\s+\}$/, ''); 1745 | 1746 | var spaces = str.match(/^\n?( *)/)[1].length 1747 | , re = new RegExp('^ {' + spaces + '}', 'gm'); 1748 | 1749 | str = str 1750 | .replace(re, '') 1751 | .replace(/^\s+/, ''); 1752 | 1753 | return str; 1754 | } 1755 | 1756 | }); // module: reporters/html.js 1757 | 1758 | require.register("reporters/index.js", function(module, exports, require){ 1759 | 1760 | exports.Base = require('./base'); 1761 | exports.Dot = require('./dot'); 1762 | exports.Doc = require('./doc'); 1763 | exports.TAP = require('./tap'); 1764 | exports.JSON = require('./json'); 1765 | exports.HTML = require('./html'); 1766 | exports.List = require('./list'); 1767 | exports.Min = require('./min'); 1768 | exports.Spec = require('./spec'); 1769 | exports.Progress = require('./progress'); 1770 | exports.Landing = require('./landing'); 1771 | exports.JSONCov = require('./json-cov'); 1772 | exports.HTMLCov = require('./html-cov'); 1773 | exports.JSONStream = require('./json-stream'); 1774 | exports.XUnit = require('./xunit') 1775 | exports.Teamcity = require('./teamcity') 1776 | 1777 | }); // module: reporters/index.js 1778 | 1779 | require.register("reporters/json-cov.js", function(module, exports, require){ 1780 | 1781 | /** 1782 | * Module dependencies. 1783 | */ 1784 | 1785 | var Base = require('./base'); 1786 | 1787 | /** 1788 | * Expose `JSONCov`. 1789 | */ 1790 | 1791 | exports = module.exports = JSONCov; 1792 | 1793 | /** 1794 | * Initialize a new `JsCoverage` reporter. 1795 | * 1796 | * @param {Runner} runner 1797 | * @param {Boolean} output 1798 | * @api public 1799 | */ 1800 | 1801 | function JSONCov(runner, output) { 1802 | var self = this 1803 | , output = 1 == arguments.length ? true : output; 1804 | 1805 | Base.call(this, runner); 1806 | 1807 | var tests = [] 1808 | , failures = [] 1809 | , passes = []; 1810 | 1811 | runner.on('test end', function(test){ 1812 | tests.push(test); 1813 | }); 1814 | 1815 | runner.on('pass', function(test){ 1816 | passes.push(test); 1817 | }); 1818 | 1819 | runner.on('fail', function(test){ 1820 | failures.push(test); 1821 | }); 1822 | 1823 | runner.on('end', function(){ 1824 | var cov = global._$jscoverage || {}; 1825 | var result = self.cov = map(cov); 1826 | result.stats = self.stats; 1827 | result.tests = tests.map(clean); 1828 | result.failures = failures.map(clean); 1829 | result.passes = passes.map(clean); 1830 | if (!output) return; 1831 | process.stdout.write(JSON.stringify(result, null, 2 )); 1832 | }); 1833 | } 1834 | 1835 | /** 1836 | * Map jscoverage data to a JSON structure 1837 | * suitable for reporting. 1838 | * 1839 | * @param {Object} cov 1840 | * @return {Object} 1841 | * @api private 1842 | */ 1843 | 1844 | function map(cov) { 1845 | var ret = { 1846 | instrumentation: 'node-jscoverage' 1847 | , sloc: 0 1848 | , hits: 0 1849 | , misses: 0 1850 | , coverage: 0 1851 | , files: [] 1852 | }; 1853 | 1854 | for (var filename in cov) { 1855 | var data = coverage(filename, cov[filename]); 1856 | ret.files.push(data); 1857 | ret.hits += data.hits; 1858 | ret.misses += data.misses; 1859 | ret.sloc += data.sloc; 1860 | } 1861 | 1862 | if (ret.sloc > 0) { 1863 | ret.coverage = (ret.hits / ret.sloc) * 100; 1864 | } 1865 | 1866 | return ret; 1867 | }; 1868 | 1869 | /** 1870 | * Map jscoverage data for a single source file 1871 | * to a JSON structure suitable for reporting. 1872 | * 1873 | * @param {String} filename name of the source file 1874 | * @param {Object} data jscoverage coverage data 1875 | * @return {Object} 1876 | * @api private 1877 | */ 1878 | 1879 | function coverage(filename, data) { 1880 | var ret = { 1881 | filename: filename, 1882 | coverage: 0, 1883 | hits: 0, 1884 | misses: 0, 1885 | sloc: 0, 1886 | source: {} 1887 | }; 1888 | 1889 | data.source.forEach(function(line, num){ 1890 | num++; 1891 | 1892 | if (data[num] === 0) { 1893 | ret.misses++; 1894 | ret.sloc++; 1895 | } else if (data[num] !== undefined) { 1896 | ret.hits++; 1897 | ret.sloc++; 1898 | } 1899 | 1900 | ret.source[num] = { 1901 | source: line 1902 | , coverage: data[num] === undefined 1903 | ? '' 1904 | : data[num] 1905 | }; 1906 | }); 1907 | 1908 | ret.coverage = ret.hits / ret.sloc * 100; 1909 | 1910 | return ret; 1911 | } 1912 | 1913 | /** 1914 | * Return a plain-object representation of `test` 1915 | * free of cyclic properties etc. 1916 | * 1917 | * @param {Object} test 1918 | * @return {Object} 1919 | * @api private 1920 | */ 1921 | 1922 | function clean(test) { 1923 | return { 1924 | title: test.title 1925 | , fullTitle: test.fullTitle() 1926 | , duration: test.duration 1927 | } 1928 | } 1929 | 1930 | }); // module: reporters/json-cov.js 1931 | 1932 | require.register("reporters/json-stream.js", function(module, exports, require){ 1933 | 1934 | /** 1935 | * Module dependencies. 1936 | */ 1937 | 1938 | var Base = require('./base') 1939 | , color = Base.color; 1940 | 1941 | /** 1942 | * Expose `List`. 1943 | */ 1944 | 1945 | exports = module.exports = List; 1946 | 1947 | /** 1948 | * Initialize a new `List` test reporter. 1949 | * 1950 | * @param {Runner} runner 1951 | * @api public 1952 | */ 1953 | 1954 | function List(runner) { 1955 | Base.call(this, runner); 1956 | 1957 | var self = this 1958 | , stats = this.stats 1959 | , total = runner.total; 1960 | 1961 | runner.on('start', function(){ 1962 | console.log(JSON.stringify(['start', { total: total }])); 1963 | }); 1964 | 1965 | runner.on('pass', function(test){ 1966 | console.log(JSON.stringify(['pass', clean(test)])); 1967 | }); 1968 | 1969 | runner.on('fail', function(test, err){ 1970 | console.log(JSON.stringify(['fail', clean(test)])); 1971 | }); 1972 | 1973 | runner.on('end', function(){ 1974 | process.stdout.write(JSON.stringify(['end', self.stats])); 1975 | }); 1976 | } 1977 | 1978 | /** 1979 | * Return a plain-object representation of `test` 1980 | * free of cyclic properties etc. 1981 | * 1982 | * @param {Object} test 1983 | * @return {Object} 1984 | * @api private 1985 | */ 1986 | 1987 | function clean(test) { 1988 | return { 1989 | title: test.title 1990 | , fullTitle: test.fullTitle() 1991 | , duration: test.duration 1992 | } 1993 | } 1994 | }); // module: reporters/json-stream.js 1995 | 1996 | require.register("reporters/json.js", function(module, exports, require){ 1997 | 1998 | /** 1999 | * Module dependencies. 2000 | */ 2001 | 2002 | var Base = require('./base') 2003 | , cursor = Base.cursor 2004 | , color = Base.color; 2005 | 2006 | /** 2007 | * Expose `JSON`. 2008 | */ 2009 | 2010 | exports = module.exports = JSONReporter; 2011 | 2012 | /** 2013 | * Initialize a new `JSON` reporter. 2014 | * 2015 | * @param {Runner} runner 2016 | * @api public 2017 | */ 2018 | 2019 | function JSONReporter(runner) { 2020 | var self = this; 2021 | Base.call(this, runner); 2022 | 2023 | var tests = [] 2024 | , failures = [] 2025 | , passes = []; 2026 | 2027 | runner.on('test end', function(test){ 2028 | tests.push(test); 2029 | }); 2030 | 2031 | runner.on('pass', function(test){ 2032 | passes.push(test); 2033 | }); 2034 | 2035 | runner.on('fail', function(test){ 2036 | failures.push(test); 2037 | }); 2038 | 2039 | runner.on('end', function(){ 2040 | var obj = { 2041 | stats: self.stats 2042 | , tests: tests.map(clean) 2043 | , failures: failures.map(clean) 2044 | , passes: passes.map(clean) 2045 | }; 2046 | 2047 | process.stdout.write(JSON.stringify(obj, null, 2)); 2048 | }); 2049 | } 2050 | 2051 | /** 2052 | * Return a plain-object representation of `test` 2053 | * free of cyclic properties etc. 2054 | * 2055 | * @param {Object} test 2056 | * @return {Object} 2057 | * @api private 2058 | */ 2059 | 2060 | function clean(test) { 2061 | return { 2062 | title: test.title 2063 | , fullTitle: test.fullTitle() 2064 | , duration: test.duration 2065 | } 2066 | } 2067 | }); // module: reporters/json.js 2068 | 2069 | require.register("reporters/landing.js", function(module, exports, require){ 2070 | 2071 | /** 2072 | * Module dependencies. 2073 | */ 2074 | 2075 | var Base = require('./base') 2076 | , cursor = Base.cursor 2077 | , color = Base.color; 2078 | 2079 | /** 2080 | * Expose `Landing`. 2081 | */ 2082 | 2083 | exports = module.exports = Landing; 2084 | 2085 | /** 2086 | * Airplane color. 2087 | */ 2088 | 2089 | Base.colors.plane = 0; 2090 | 2091 | /** 2092 | * Airplane crash color. 2093 | */ 2094 | 2095 | Base.colors['plane crash'] = 31; 2096 | 2097 | /** 2098 | * Runway color. 2099 | */ 2100 | 2101 | Base.colors.runway = 90; 2102 | 2103 | /** 2104 | * Initialize a new `Landing` reporter. 2105 | * 2106 | * @param {Runner} runner 2107 | * @api public 2108 | */ 2109 | 2110 | function Landing(runner) { 2111 | Base.call(this, runner); 2112 | 2113 | var self = this 2114 | , stats = this.stats 2115 | , width = Base.window.width * .75 | 0 2116 | , total = runner.total 2117 | , stream = process.stdout 2118 | , plane = color('plane', '✈') 2119 | , crashed = -1 2120 | , n = 0; 2121 | 2122 | function runway() { 2123 | var buf = Array(width).join('-'); 2124 | return ' ' + color('runway', buf); 2125 | } 2126 | 2127 | runner.on('start', function(){ 2128 | stream.write('\n '); 2129 | cursor.hide(); 2130 | }); 2131 | 2132 | runner.on('test end', function(test){ 2133 | // check if the plane crashed 2134 | var col = -1 == crashed 2135 | ? width * ++n / total | 0 2136 | : crashed; 2137 | 2138 | // show the crash 2139 | if ('failed' == test.state) { 2140 | plane = color('plane crash', '✈'); 2141 | crashed = col; 2142 | } 2143 | 2144 | // render landing strip 2145 | stream.write('\033[4F\n\n'); 2146 | stream.write(runway()); 2147 | stream.write('\n '); 2148 | stream.write(color('runway', Array(col).join('⋅'))); 2149 | stream.write(plane) 2150 | stream.write(color('runway', Array(width - col).join('⋅') + '\n')); 2151 | stream.write(runway()); 2152 | stream.write('\033[0m'); 2153 | }); 2154 | 2155 | runner.on('end', function(){ 2156 | cursor.show(); 2157 | console.log(); 2158 | self.epilogue(); 2159 | }); 2160 | } 2161 | 2162 | /** 2163 | * Inherit from `Base.prototype`. 2164 | */ 2165 | 2166 | Landing.prototype = new Base; 2167 | Landing.prototype.constructor = Landing; 2168 | 2169 | }); // module: reporters/landing.js 2170 | 2171 | require.register("reporters/list.js", function(module, exports, require){ 2172 | 2173 | /** 2174 | * Module dependencies. 2175 | */ 2176 | 2177 | var Base = require('./base') 2178 | , cursor = Base.cursor 2179 | , color = Base.color; 2180 | 2181 | /** 2182 | * Expose `List`. 2183 | */ 2184 | 2185 | exports = module.exports = List; 2186 | 2187 | /** 2188 | * Initialize a new `List` test reporter. 2189 | * 2190 | * @param {Runner} runner 2191 | * @api public 2192 | */ 2193 | 2194 | function List(runner) { 2195 | Base.call(this, runner); 2196 | 2197 | var self = this 2198 | , stats = this.stats 2199 | , n = 0; 2200 | 2201 | runner.on('start', function(){ 2202 | console.log(); 2203 | }); 2204 | 2205 | runner.on('test', function(test){ 2206 | process.stdout.write(color('pass', ' ' + test.fullTitle() + ': ')); 2207 | }); 2208 | 2209 | runner.on('pending', function(test){ 2210 | var fmt = color('checkmark', ' -') 2211 | + color('pending', ' %s'); 2212 | console.log(fmt, test.fullTitle()); 2213 | }); 2214 | 2215 | runner.on('pass', function(test){ 2216 | var fmt = color('checkmark', ' ✓') 2217 | + color('pass', ' %s: ') 2218 | + color(test.speed, '%dms'); 2219 | cursor.CR(); 2220 | console.log(fmt, test.fullTitle(), test.duration); 2221 | }); 2222 | 2223 | runner.on('fail', function(test, err){ 2224 | cursor.CR(); 2225 | console.log(color('fail', ' %d) %s'), ++n, test.fullTitle()); 2226 | }); 2227 | 2228 | runner.on('end', self.epilogue.bind(self)); 2229 | } 2230 | 2231 | /** 2232 | * Inherit from `Base.prototype`. 2233 | */ 2234 | 2235 | List.prototype = new Base; 2236 | List.prototype.constructor = List; 2237 | 2238 | 2239 | }); // module: reporters/list.js 2240 | 2241 | require.register("reporters/markdown.js", function(module, exports, require){ 2242 | 2243 | /** 2244 | * Module dependencies. 2245 | */ 2246 | 2247 | var Base = require('./base') 2248 | , utils = require('../utils'); 2249 | 2250 | /** 2251 | * Expose `Markdown`. 2252 | */ 2253 | 2254 | exports = module.exports = Markdown; 2255 | 2256 | /** 2257 | * Initialize a new `Markdown` reporter. 2258 | * 2259 | * @param {Runner} runner 2260 | * @api public 2261 | */ 2262 | 2263 | function Markdown(runner) { 2264 | Base.call(this, runner); 2265 | 2266 | var self = this 2267 | , stats = this.stats 2268 | , total = runner.total 2269 | , level = 0 2270 | , buf = ''; 2271 | 2272 | function title(str) { 2273 | return Array(level).join('#') + ' ' + str; 2274 | } 2275 | 2276 | function indent() { 2277 | return Array(level).join(' '); 2278 | } 2279 | 2280 | function mapTOC(suite, obj) { 2281 | var ret = obj; 2282 | obj = obj[suite.title] = obj[suite.title] || { suite: suite }; 2283 | suite.suites.forEach(function(suite){ 2284 | mapTOC(suite, obj); 2285 | }); 2286 | return ret; 2287 | } 2288 | 2289 | function stringifyTOC(obj, level) { 2290 | ++level; 2291 | var buf = ''; 2292 | var link; 2293 | for (var key in obj) { 2294 | if ('suite' == key) continue; 2295 | if (key) link = ' - [' + key + '](#' + utils.slug(obj[key].suite.fullTitle()) + ')\n'; 2296 | if (key) buf += Array(level).join(' ') + link; 2297 | buf += stringifyTOC(obj[key], level); 2298 | } 2299 | --level; 2300 | return buf; 2301 | } 2302 | 2303 | function generateTOC(suite) { 2304 | var obj = mapTOC(suite, {}); 2305 | return stringifyTOC(obj, 0); 2306 | } 2307 | 2308 | generateTOC(runner.suite); 2309 | 2310 | runner.on('suite', function(suite){ 2311 | ++level; 2312 | var slug = utils.slug(suite.fullTitle()); 2313 | buf += '' + '\n'; 2314 | buf += title(suite.title) + '\n'; 2315 | }); 2316 | 2317 | runner.on('suite end', function(suite){ 2318 | --level; 2319 | }); 2320 | 2321 | runner.on('pass', function(test){ 2322 | var code = clean(test.fn.toString()); 2323 | buf += test.title + '.\n'; 2324 | buf += '\n```js'; 2325 | buf += code + '\n'; 2326 | buf += '```\n\n'; 2327 | }); 2328 | 2329 | runner.on('end', function(){ 2330 | process.stdout.write('# TOC\n'); 2331 | process.stdout.write(generateTOC(runner.suite)); 2332 | process.stdout.write(buf); 2333 | }); 2334 | } 2335 | 2336 | /** 2337 | * Strip the function definition from `str`, 2338 | * and re-indent for pre whitespace. 2339 | */ 2340 | 2341 | function clean(str) { 2342 | str = str 2343 | .replace(/^function *\(.*\) *{/, '') 2344 | .replace(/\s+\}$/, ''); 2345 | 2346 | var spaces = str.match(/^\n?( *)/)[1].length 2347 | , re = new RegExp('^ {' + spaces + '}', 'gm'); 2348 | 2349 | str = str.replace(re, ''); 2350 | 2351 | return str; 2352 | } 2353 | }); // module: reporters/markdown.js 2354 | 2355 | require.register("reporters/min.js", function(module, exports, require){ 2356 | /** 2357 | * Module dependencies. 2358 | */ 2359 | 2360 | var Base = require('./base'); 2361 | 2362 | /** 2363 | * Expose `Min`. 2364 | */ 2365 | 2366 | exports = module.exports = Min; 2367 | 2368 | /** 2369 | * Initialize a new `Min` minimal test reporter (best used with --watch). 2370 | * 2371 | * @param {Runner} runner 2372 | * @api public 2373 | */ 2374 | 2375 | function Min(runner) { 2376 | Base.call(this, runner); 2377 | 2378 | runner.on('start', function(){ 2379 | // clear screen 2380 | process.stdout.write('\033[2J'); 2381 | // set cursor position 2382 | process.stdout.write('\033[1;3H'); 2383 | }); 2384 | 2385 | runner.on('end', this.epilogue.bind(this)); 2386 | } 2387 | 2388 | /** 2389 | * Inherit from `Base.prototype`. 2390 | */ 2391 | 2392 | Min.prototype = new Base; 2393 | Min.prototype.constructor = Min; 2394 | 2395 | }); // module: reporters/min.js 2396 | 2397 | require.register("reporters/progress.js", function(module, exports, require){ 2398 | 2399 | /** 2400 | * Module dependencies. 2401 | */ 2402 | 2403 | var Base = require('./base') 2404 | , cursor = Base.cursor 2405 | , color = Base.color; 2406 | 2407 | /** 2408 | * Expose `Progress`. 2409 | */ 2410 | 2411 | exports = module.exports = Progress; 2412 | 2413 | /** 2414 | * General progress bar color. 2415 | */ 2416 | 2417 | Base.colors.progress = 90; 2418 | 2419 | /** 2420 | * Initialize a new `Progress` bar test reporter. 2421 | * 2422 | * @param {Runner} runner 2423 | * @param {Object} options 2424 | * @api public 2425 | */ 2426 | 2427 | function Progress(runner, options) { 2428 | Base.call(this, runner); 2429 | 2430 | var self = this 2431 | , options = options || {} 2432 | , stats = this.stats 2433 | , width = Base.window.width * .50 | 0 2434 | , total = runner.total 2435 | , complete = 0 2436 | , max = Math.max; 2437 | 2438 | // default chars 2439 | options.open = options.open || '['; 2440 | options.complete = options.complete || '▬'; 2441 | options.incomplete = options.incomplete || '⋅'; 2442 | options.close = options.close || ']'; 2443 | options.verbose = false; 2444 | 2445 | // tests started 2446 | runner.on('start', function(){ 2447 | console.log(); 2448 | cursor.hide(); 2449 | }); 2450 | 2451 | // tests complete 2452 | runner.on('test end', function(){ 2453 | complete++; 2454 | var incomplete = total - complete 2455 | , percent = complete / total 2456 | , n = width * percent | 0 2457 | , i = width - n; 2458 | 2459 | cursor.CR(); 2460 | process.stdout.write('\033[J'); 2461 | process.stdout.write(color('progress', ' ' + options.open)); 2462 | process.stdout.write(Array(n).join(options.complete)); 2463 | process.stdout.write(Array(i).join(options.incomplete)); 2464 | process.stdout.write(color('progress', options.close)); 2465 | if (options.verbose) { 2466 | process.stdout.write(color('progress', ' ' + complete + ' of ' + total)); 2467 | } 2468 | }); 2469 | 2470 | // tests are complete, output some stats 2471 | // and the failures if any 2472 | runner.on('end', function(){ 2473 | cursor.show(); 2474 | console.log(); 2475 | self.epilogue(); 2476 | }); 2477 | } 2478 | 2479 | /** 2480 | * Inherit from `Base.prototype`. 2481 | */ 2482 | 2483 | Progress.prototype = new Base; 2484 | Progress.prototype.constructor = Progress; 2485 | 2486 | 2487 | }); // module: reporters/progress.js 2488 | 2489 | require.register("reporters/spec.js", function(module, exports, require){ 2490 | 2491 | /** 2492 | * Module dependencies. 2493 | */ 2494 | 2495 | var Base = require('./base') 2496 | , cursor = Base.cursor 2497 | , color = Base.color; 2498 | 2499 | /** 2500 | * Expose `Spec`. 2501 | */ 2502 | 2503 | exports = module.exports = Spec; 2504 | 2505 | /** 2506 | * Initialize a new `Spec` test reporter. 2507 | * 2508 | * @param {Runner} runner 2509 | * @api public 2510 | */ 2511 | 2512 | function Spec(runner) { 2513 | Base.call(this, runner); 2514 | 2515 | var self = this 2516 | , stats = this.stats 2517 | , indents = 0 2518 | , n = 0; 2519 | 2520 | function indent() { 2521 | return Array(indents).join(' ') 2522 | } 2523 | 2524 | runner.on('start', function(){ 2525 | console.log(); 2526 | }); 2527 | 2528 | runner.on('suite', function(suite){ 2529 | ++indents; 2530 | console.log(color('suite', '%s%s'), indent(), suite.title); 2531 | }); 2532 | 2533 | runner.on('suite end', function(suite){ 2534 | --indents; 2535 | if (1 == indents) console.log(); 2536 | }); 2537 | 2538 | runner.on('test', function(test){ 2539 | process.stdout.write(indent() + color('pass', ' ◦ ' + test.title + ': ')); 2540 | }); 2541 | 2542 | runner.on('pending', function(test){ 2543 | var fmt = indent() + color('pending', ' - %s'); 2544 | console.log(fmt, test.title); 2545 | }); 2546 | 2547 | runner.on('pass', function(test){ 2548 | if ('fast' == test.speed) { 2549 | var fmt = indent() 2550 | + color('checkmark', ' ✓') 2551 | + color('pass', ' %s '); 2552 | cursor.CR(); 2553 | console.log(fmt, test.title); 2554 | } else { 2555 | var fmt = indent() 2556 | + color('checkmark', ' ✓') 2557 | + color('pass', ' %s ') 2558 | + color(test.speed, '(%dms)'); 2559 | cursor.CR(); 2560 | console.log(fmt, test.title, test.duration); 2561 | } 2562 | }); 2563 | 2564 | runner.on('fail', function(test, err){ 2565 | cursor.CR(); 2566 | console.log(indent() + color('fail', ' %d) %s'), ++n, test.title); 2567 | }); 2568 | 2569 | runner.on('end', self.epilogue.bind(self)); 2570 | } 2571 | 2572 | /** 2573 | * Inherit from `Base.prototype`. 2574 | */ 2575 | 2576 | Spec.prototype = new Base; 2577 | Spec.prototype.constructor = Spec; 2578 | 2579 | 2580 | }); // module: reporters/spec.js 2581 | 2582 | require.register("reporters/tap.js", function(module, exports, require){ 2583 | 2584 | /** 2585 | * Module dependencies. 2586 | */ 2587 | 2588 | var Base = require('./base') 2589 | , cursor = Base.cursor 2590 | , color = Base.color; 2591 | 2592 | /** 2593 | * Expose `TAP`. 2594 | */ 2595 | 2596 | exports = module.exports = TAP; 2597 | 2598 | /** 2599 | * Initialize a new `TAP` reporter. 2600 | * 2601 | * @param {Runner} runner 2602 | * @api public 2603 | */ 2604 | 2605 | function TAP(runner) { 2606 | Base.call(this, runner); 2607 | 2608 | var self = this 2609 | , stats = this.stats 2610 | , total = runner.total 2611 | , n = 1; 2612 | 2613 | runner.on('start', function(){ 2614 | console.log('%d..%d', 1, total); 2615 | }); 2616 | 2617 | runner.on('test end', function(){ 2618 | ++n; 2619 | }); 2620 | 2621 | runner.on('pending', function(test){ 2622 | console.log('ok %d %s # SKIP -', n, title(test)); 2623 | }); 2624 | 2625 | runner.on('pass', function(test){ 2626 | console.log('ok %d %s', n, title(test)); 2627 | }); 2628 | 2629 | runner.on('fail', function(test, err){ 2630 | console.log('not ok %d %s', n, title(test)); 2631 | console.log(err.stack.replace(/^/gm, ' ')); 2632 | }); 2633 | } 2634 | 2635 | /** 2636 | * Return a TAP-safe title of `test` 2637 | * 2638 | * @param {Object} test 2639 | * @return {String} 2640 | * @api private 2641 | */ 2642 | 2643 | function title(test) { 2644 | return test.fullTitle().replace(/#/g, ''); 2645 | } 2646 | 2647 | }); // module: reporters/tap.js 2648 | 2649 | require.register("reporters/teamcity.js", function(module, exports, require){ 2650 | 2651 | /** 2652 | * Module dependencies. 2653 | */ 2654 | 2655 | var Base = require('./base'); 2656 | 2657 | /** 2658 | * Expose `Teamcity`. 2659 | */ 2660 | 2661 | exports = module.exports = Teamcity; 2662 | 2663 | /** 2664 | * Initialize a new `Teamcity` reporter. 2665 | * 2666 | * @param {Runner} runner 2667 | * @api public 2668 | */ 2669 | 2670 | function Teamcity(runner) { 2671 | Base.call(this, runner); 2672 | var stats = this.stats; 2673 | 2674 | runner.on('start', function() { 2675 | console.log("##teamcity[testSuiteStarted name='mocha.suite']"); 2676 | }); 2677 | 2678 | runner.on('test', function(test) { 2679 | console.log("##teamcity[testStarted name='%s']", escape(test.fullTitle())); 2680 | }); 2681 | 2682 | runner.on('fail', function(test, err) { 2683 | console.log("##teamcity[testFailed name='%s' message='%s']", escape(test.fullTitle()), escape(err.message)); 2684 | }); 2685 | 2686 | runner.on('pending', function(test) { 2687 | console.log("##teamcity[testIgnored name='%s' message='pending']", escape(test.fullTitle())); 2688 | }); 2689 | 2690 | runner.on('test end', function(test) { 2691 | console.log("##teamcity[testFinished name='%s' duration='%s']", escape(test.fullTitle()), test.duration); 2692 | }); 2693 | 2694 | runner.on('end', function() { 2695 | console.log("##teamcity[testSuiteFinished name='mocha.suite' duration='%s']", stats.duration); 2696 | }); 2697 | } 2698 | 2699 | /** 2700 | * Escape the given `str`. 2701 | */ 2702 | 2703 | function escape(str) { 2704 | return str.replace(/'/g, "|'"); 2705 | } 2706 | }); // module: reporters/teamcity.js 2707 | 2708 | require.register("reporters/xunit.js", function(module, exports, require){ 2709 | 2710 | /** 2711 | * Module dependencies. 2712 | */ 2713 | 2714 | var Base = require('./base') 2715 | , utils = require('../utils') 2716 | , escape = utils.escape; 2717 | 2718 | /** 2719 | * Expose `XUnit`. 2720 | */ 2721 | 2722 | exports = module.exports = XUnit; 2723 | 2724 | /** 2725 | * Initialize a new `XUnit` reporter. 2726 | * 2727 | * @param {Runner} runner 2728 | * @api public 2729 | */ 2730 | 2731 | function XUnit(runner) { 2732 | Base.call(this, runner); 2733 | var stats = this.stats 2734 | , tests = [] 2735 | , self = this; 2736 | 2737 | runner.on('test end', function(test){ 2738 | tests.push(test); 2739 | }); 2740 | 2741 | runner.on('end', function(){ 2742 | console.log(tag('testsuite', { 2743 | name: 'Mocha Tests' 2744 | , tests: stats.tests 2745 | , failures: stats.failures 2746 | , errors: stats.failures 2747 | , skip: stats.tests - stats.failures - stats.passes 2748 | , timestamp: (new Date).toUTCString() 2749 | , time: stats.duration / 1000 2750 | }, false)); 2751 | 2752 | tests.forEach(test); 2753 | console.log(''); 2754 | }); 2755 | } 2756 | 2757 | /** 2758 | * Inherit from `Base.prototype`. 2759 | */ 2760 | 2761 | XUnit.prototype = new Base; 2762 | XUnit.prototype.constructor = XUnit; 2763 | 2764 | 2765 | /** 2766 | * Output tag for the given `test.` 2767 | */ 2768 | 2769 | function test(test) { 2770 | var attrs = { 2771 | classname: test.parent.fullTitle() 2772 | , name: test.title 2773 | , time: test.duration / 1000 2774 | }; 2775 | 2776 | if ('failed' == test.state) { 2777 | var err = test.err; 2778 | attrs.message = escape(err.message); 2779 | console.log(tag('testcase', attrs, false, tag('failure', attrs, false, cdata(err.stack)))); 2780 | } else if (test.pending) { 2781 | console.log(tag('testcase', attrs, false, tag('skipped', {}, true))); 2782 | } else { 2783 | console.log(tag('testcase', attrs, true) ); 2784 | } 2785 | } 2786 | 2787 | /** 2788 | * HTML tag helper. 2789 | */ 2790 | 2791 | function tag(name, attrs, close, content) { 2792 | var end = close ? '/>' : '>' 2793 | , pairs = [] 2794 | , tag; 2795 | 2796 | for (var key in attrs) { 2797 | pairs.push(key + '="' + escape(attrs[key]) + '"'); 2798 | } 2799 | 2800 | tag = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end; 2801 | if (content) tag += content + ''; 2811 | } 2812 | 2813 | }); // module: reporters/xunit.js 2814 | 2815 | require.register("runnable.js", function(module, exports, require){ 2816 | 2817 | /** 2818 | * Module dependencies. 2819 | */ 2820 | 2821 | var EventEmitter = require('browser/events').EventEmitter 2822 | , debug = require('browser/debug')('runnable'); 2823 | 2824 | /** 2825 | * Expose `Runnable`. 2826 | */ 2827 | 2828 | module.exports = Runnable; 2829 | 2830 | /** 2831 | * Initialize a new `Runnable` with the given `title` and callback `fn`. 2832 | * 2833 | * @param {String} title 2834 | * @param {Function} fn 2835 | * @api private 2836 | */ 2837 | 2838 | function Runnable(title, fn) { 2839 | this.title = title; 2840 | this.fn = fn; 2841 | this.async = fn && fn.length; 2842 | this.sync = ! this.async; 2843 | this._timeout = 2000; 2844 | this.timedOut = false; 2845 | } 2846 | 2847 | /** 2848 | * Inherit from `EventEmitter.prototype`. 2849 | */ 2850 | 2851 | Runnable.prototype = new EventEmitter; 2852 | Runnable.prototype.constructor = Runnable; 2853 | 2854 | 2855 | /** 2856 | * Set & get timeout `ms`. 2857 | * 2858 | * @param {Number} ms 2859 | * @return {Runnable|Number} ms or self 2860 | * @api private 2861 | */ 2862 | 2863 | Runnable.prototype.timeout = function(ms){ 2864 | if (0 == arguments.length) return this._timeout; 2865 | debug('timeout %d', ms); 2866 | this._timeout = ms; 2867 | if (this.timer) this.resetTimeout(); 2868 | return this; 2869 | }; 2870 | 2871 | /** 2872 | * Return the full title generated by recursively 2873 | * concatenating the parent's full title. 2874 | * 2875 | * @return {String} 2876 | * @api public 2877 | */ 2878 | 2879 | Runnable.prototype.fullTitle = function(){ 2880 | return this.parent.fullTitle() + ' ' + this.title; 2881 | }; 2882 | 2883 | /** 2884 | * Clear the timeout. 2885 | * 2886 | * @api private 2887 | */ 2888 | 2889 | Runnable.prototype.clearTimeout = function(){ 2890 | clearTimeout(this.timer); 2891 | }; 2892 | 2893 | /** 2894 | * Reset the timeout. 2895 | * 2896 | * @api private 2897 | */ 2898 | 2899 | Runnable.prototype.resetTimeout = function(){ 2900 | var self = this 2901 | , ms = this.timeout(); 2902 | 2903 | this.clearTimeout(); 2904 | if (ms) { 2905 | this.timer = setTimeout(function(){ 2906 | self.callback(new Error('timeout of ' + ms + 'ms exceeded')); 2907 | self.timedOut = true; 2908 | }, ms); 2909 | } 2910 | }; 2911 | 2912 | /** 2913 | * Run the test and invoke `fn(err)`. 2914 | * 2915 | * @param {Function} fn 2916 | * @api private 2917 | */ 2918 | 2919 | Runnable.prototype.run = function(fn){ 2920 | var self = this 2921 | , ms = this.timeout() 2922 | , start = new Date 2923 | , ctx = this.ctx 2924 | , finished 2925 | , emitted; 2926 | 2927 | if (ctx) ctx.runnable(this); 2928 | 2929 | // timeout 2930 | if (this.async) { 2931 | if (ms) { 2932 | this.timer = setTimeout(function(){ 2933 | done(new Error('timeout of ' + ms + 'ms exceeded')); 2934 | self.timedOut = true; 2935 | }, ms); 2936 | } 2937 | } 2938 | 2939 | // called multiple times 2940 | function multiple() { 2941 | if (emitted) return; 2942 | emitted = true; 2943 | self.emit('error', new Error('done() called multiple times')); 2944 | } 2945 | 2946 | // finished 2947 | function done(err) { 2948 | if (self.timedOut) return; 2949 | if (finished) return multiple(); 2950 | self.clearTimeout(); 2951 | self.duration = new Date - start; 2952 | finished = true; 2953 | fn(err); 2954 | } 2955 | 2956 | // for .resetTimeout() 2957 | this.callback = done; 2958 | 2959 | // async 2960 | if (this.async) { 2961 | try { 2962 | this.fn.call(ctx, function(err){ 2963 | if (err instanceof Error) return done(err); 2964 | if (null != err) return done(new Error('done() invoked with non-Error: ' + err)); 2965 | done(); 2966 | }); 2967 | } catch (err) { 2968 | done(err); 2969 | } 2970 | return; 2971 | } 2972 | 2973 | // sync 2974 | try { 2975 | if (!this.pending) this.fn.call(ctx); 2976 | this.duration = new Date - start; 2977 | fn(); 2978 | } catch (err) { 2979 | fn(err); 2980 | } 2981 | }; 2982 | 2983 | }); // module: runnable.js 2984 | 2985 | require.register("runner.js", function(module, exports, require){ 2986 | 2987 | /** 2988 | * Module dependencies. 2989 | */ 2990 | 2991 | var EventEmitter = require('browser/events').EventEmitter 2992 | , debug = require('browser/debug')('runner') 2993 | , Test = require('./test') 2994 | , utils = require('./utils') 2995 | , noop = function(){}; 2996 | 2997 | /** 2998 | * Expose `Runner`. 2999 | */ 3000 | 3001 | module.exports = Runner; 3002 | 3003 | /** 3004 | * Initialize a `Runner` for the given `suite`. 3005 | * 3006 | * Events: 3007 | * 3008 | * - `start` execution started 3009 | * - `end` execution complete 3010 | * - `suite` (suite) test suite execution started 3011 | * - `suite end` (suite) all tests (and sub-suites) have finished 3012 | * - `test` (test) test execution started 3013 | * - `test end` (test) test completed 3014 | * - `hook` (hook) hook execution started 3015 | * - `hook end` (hook) hook complete 3016 | * - `pass` (test) test passed 3017 | * - `fail` (test, err) test failed 3018 | * 3019 | * @api public 3020 | */ 3021 | 3022 | function Runner(suite) { 3023 | var self = this; 3024 | this._globals = []; 3025 | this.suite = suite; 3026 | this.total = suite.total(); 3027 | this.failures = 0; 3028 | this.on('test end', function(test){ self.checkGlobals(test); }); 3029 | this.on('hook end', function(hook){ self.checkGlobals(hook); }); 3030 | this.grep(/.*/); 3031 | this.globals(utils.keys(global).concat(['errno'])); 3032 | } 3033 | 3034 | /** 3035 | * Inherit from `EventEmitter.prototype`. 3036 | */ 3037 | 3038 | Runner.prototype = new EventEmitter; 3039 | Runner.prototype.constructor = Runner; 3040 | 3041 | 3042 | /** 3043 | * Run tests with full titles matching `re`. Updates runner.total 3044 | * with number of tests matched. 3045 | * 3046 | * @param {RegExp} re 3047 | * @return {Runner} for chaining 3048 | * @api public 3049 | */ 3050 | 3051 | Runner.prototype.grep = function(re){ 3052 | debug('grep %s', re); 3053 | this._grep = re; 3054 | this.total = this.grepTotal(this.suite); 3055 | return this; 3056 | }; 3057 | 3058 | /** 3059 | * Returns the number of tests matching the grep search for the 3060 | * given suite. 3061 | * 3062 | * @param {Suite} suite 3063 | * @return {Number} 3064 | * @api public 3065 | */ 3066 | 3067 | Runner.prototype.grepTotal = function(suite) { 3068 | var self = this; 3069 | var total = 0; 3070 | 3071 | suite.eachTest(function(test){ 3072 | if (self._grep.test(test.fullTitle())) total++; 3073 | }); 3074 | 3075 | return total; 3076 | }; 3077 | 3078 | /** 3079 | * Allow the given `arr` of globals. 3080 | * 3081 | * @param {Array} arr 3082 | * @return {Runner} for chaining 3083 | * @api public 3084 | */ 3085 | 3086 | Runner.prototype.globals = function(arr){ 3087 | if (0 == arguments.length) return this._globals; 3088 | debug('globals %j', arr); 3089 | utils.forEach(arr, function(arr){ 3090 | this._globals.push(arr); 3091 | }, this); 3092 | return this; 3093 | }; 3094 | 3095 | /** 3096 | * Check for global variable leaks. 3097 | * 3098 | * @api private 3099 | */ 3100 | 3101 | Runner.prototype.checkGlobals = function(test){ 3102 | if (this.ignoreLeaks) return; 3103 | 3104 | var leaks = utils.filter(utils.keys(global), function(key){ 3105 | return !~utils.indexOf(this._globals, key) && (!global.navigator || 'onerror' !== key); 3106 | }, this); 3107 | 3108 | this._globals = this._globals.concat(leaks); 3109 | 3110 | if (leaks.length > 1) { 3111 | this.fail(test, new Error('global leaks detected: ' + leaks.join(', ') + '')); 3112 | } else if (leaks.length) { 3113 | this.fail(test, new Error('global leak detected: ' + leaks[0])); 3114 | } 3115 | }; 3116 | 3117 | /** 3118 | * Fail the given `test`. 3119 | * 3120 | * @param {Test} test 3121 | * @param {Error} err 3122 | * @api private 3123 | */ 3124 | 3125 | Runner.prototype.fail = function(test, err){ 3126 | ++this.failures; 3127 | test.state = 'failed'; 3128 | this.emit('fail', test, err); 3129 | }; 3130 | 3131 | /** 3132 | * Fail the given `hook` with `err`. 3133 | * 3134 | * Hook failures (currently) hard-end due 3135 | * to that fact that a failing hook will 3136 | * surely cause subsequent tests to fail, 3137 | * causing jumbled reporting. 3138 | * 3139 | * @param {Hook} hook 3140 | * @param {Error} err 3141 | * @api private 3142 | */ 3143 | 3144 | Runner.prototype.failHook = function(hook, err){ 3145 | this.fail(hook, err); 3146 | this.emit('end'); 3147 | }; 3148 | 3149 | /** 3150 | * Run hook `name` callbacks and then invoke `fn()`. 3151 | * 3152 | * @param {String} name 3153 | * @param {Function} function 3154 | * @api private 3155 | */ 3156 | 3157 | Runner.prototype.hook = function(name, fn){ 3158 | var suite = this.suite 3159 | , hooks = suite['_' + name] 3160 | , ms = suite._timeout 3161 | , self = this 3162 | , timer; 3163 | 3164 | function next(i) { 3165 | var hook = hooks[i]; 3166 | if (!hook) return fn(); 3167 | self.currentRunnable = hook; 3168 | 3169 | self.emit('hook', hook); 3170 | 3171 | hook.on('error', function(err){ 3172 | self.failHook(hook, err); 3173 | }); 3174 | 3175 | hook.run(function(err){ 3176 | hook.removeAllListeners('error'); 3177 | if (err) return self.failHook(hook, err); 3178 | self.emit('hook end', hook); 3179 | next(++i); 3180 | }); 3181 | } 3182 | 3183 | process.nextTick(function(){ 3184 | next(0); 3185 | }); 3186 | }; 3187 | 3188 | /** 3189 | * Run hook `name` for the given array of `suites` 3190 | * in order, and callback `fn(err)`. 3191 | * 3192 | * @param {String} name 3193 | * @param {Array} suites 3194 | * @param {Function} fn 3195 | * @api private 3196 | */ 3197 | 3198 | Runner.prototype.hooks = function(name, suites, fn){ 3199 | var self = this 3200 | , orig = this.suite; 3201 | 3202 | function next(suite) { 3203 | self.suite = suite; 3204 | 3205 | if (!suite) { 3206 | self.suite = orig; 3207 | return fn(); 3208 | } 3209 | 3210 | self.hook(name, function(err){ 3211 | if (err) { 3212 | self.suite = orig; 3213 | return fn(err); 3214 | } 3215 | 3216 | next(suites.pop()); 3217 | }); 3218 | } 3219 | 3220 | next(suites.pop()); 3221 | }; 3222 | 3223 | /** 3224 | * Run hooks from the top level down. 3225 | * 3226 | * @param {String} name 3227 | * @param {Function} fn 3228 | * @api private 3229 | */ 3230 | 3231 | Runner.prototype.hookUp = function(name, fn){ 3232 | var suites = [this.suite].concat(this.parents()).reverse(); 3233 | this.hooks(name, suites, fn); 3234 | }; 3235 | 3236 | /** 3237 | * Run hooks from the bottom up. 3238 | * 3239 | * @param {String} name 3240 | * @param {Function} fn 3241 | * @api private 3242 | */ 3243 | 3244 | Runner.prototype.hookDown = function(name, fn){ 3245 | var suites = [this.suite].concat(this.parents()); 3246 | this.hooks(name, suites, fn); 3247 | }; 3248 | 3249 | /** 3250 | * Return an array of parent Suites from 3251 | * closest to furthest. 3252 | * 3253 | * @return {Array} 3254 | * @api private 3255 | */ 3256 | 3257 | Runner.prototype.parents = function(){ 3258 | var suite = this.suite 3259 | , suites = []; 3260 | while (suite = suite.parent) suites.push(suite); 3261 | return suites; 3262 | }; 3263 | 3264 | /** 3265 | * Run the current test and callback `fn(err)`. 3266 | * 3267 | * @param {Function} fn 3268 | * @api private 3269 | */ 3270 | 3271 | Runner.prototype.runTest = function(fn){ 3272 | var test = this.test 3273 | , self = this; 3274 | 3275 | try { 3276 | test.on('error', function(err){ 3277 | self.fail(test, err); 3278 | }); 3279 | test.run(fn); 3280 | } catch (err) { 3281 | fn(err); 3282 | } 3283 | }; 3284 | 3285 | /** 3286 | * Run tests in the given `suite` and invoke 3287 | * the callback `fn()` when complete. 3288 | * 3289 | * @param {Suite} suite 3290 | * @param {Function} fn 3291 | * @api private 3292 | */ 3293 | 3294 | Runner.prototype.runTests = function(suite, fn){ 3295 | var self = this 3296 | , tests = suite.tests 3297 | , test; 3298 | 3299 | function next(err) { 3300 | // if we bail after first err 3301 | if (self.failures && suite._bail) return fn(); 3302 | 3303 | // next test 3304 | test = tests.shift(); 3305 | 3306 | // all done 3307 | if (!test) return fn(); 3308 | 3309 | // grep 3310 | if (!self._grep.test(test.fullTitle())) return next(); 3311 | 3312 | // pending 3313 | if (test.pending) { 3314 | self.emit('pending', test); 3315 | self.emit('test end', test); 3316 | return next(); 3317 | } 3318 | 3319 | // execute test and hook(s) 3320 | self.emit('test', self.test = test); 3321 | self.hookDown('beforeEach', function(){ 3322 | self.currentRunnable = self.test; 3323 | self.runTest(function(err){ 3324 | test = self.test; 3325 | 3326 | if (err) { 3327 | self.fail(test, err); 3328 | self.emit('test end', test); 3329 | return self.hookUp('afterEach', next); 3330 | } 3331 | 3332 | test.state = 'passed'; 3333 | self.emit('pass', test); 3334 | self.emit('test end', test); 3335 | self.hookUp('afterEach', next); 3336 | }); 3337 | }); 3338 | } 3339 | 3340 | this.next = next; 3341 | next(); 3342 | }; 3343 | 3344 | /** 3345 | * Run the given `suite` and invoke the 3346 | * callback `fn()` when complete. 3347 | * 3348 | * @param {Suite} suite 3349 | * @param {Function} fn 3350 | * @api private 3351 | */ 3352 | 3353 | Runner.prototype.runSuite = function(suite, fn){ 3354 | var total = this.grepTotal(suite) 3355 | , self = this 3356 | , i = 0; 3357 | 3358 | debug('run suite %s', suite.fullTitle()); 3359 | 3360 | if (!total) return fn(); 3361 | 3362 | this.emit('suite', this.suite = suite); 3363 | 3364 | function next() { 3365 | var curr = suite.suites[i++]; 3366 | if (!curr) return done(); 3367 | self.runSuite(curr, next); 3368 | } 3369 | 3370 | function done() { 3371 | self.suite = suite; 3372 | self.hook('afterAll', function(){ 3373 | self.emit('suite end', suite); 3374 | fn(); 3375 | }); 3376 | } 3377 | 3378 | this.hook('beforeAll', function(){ 3379 | self.runTests(suite, next); 3380 | }); 3381 | }; 3382 | 3383 | /** 3384 | * Handle uncaught exceptions. 3385 | * 3386 | * @param {Error} err 3387 | * @api private 3388 | */ 3389 | 3390 | Runner.prototype.uncaught = function(err){ 3391 | debug('uncaught exception'); 3392 | var runnable = this.currentRunnable; 3393 | if ('failed' == runnable.state) return; 3394 | runnable.clearTimeout(); 3395 | err.uncaught = true; 3396 | this.fail(runnable, err); 3397 | 3398 | // recover from test 3399 | if ('test' == runnable.type) { 3400 | this.emit('test end', runnable); 3401 | this.hookUp('afterEach', this.next); 3402 | return; 3403 | } 3404 | 3405 | // bail on hooks 3406 | this.emit('end'); 3407 | }; 3408 | 3409 | /** 3410 | * Run the root suite and invoke `fn(failures)` 3411 | * on completion. 3412 | * 3413 | * @param {Function} fn 3414 | * @return {Runner} for chaining 3415 | * @api public 3416 | */ 3417 | 3418 | Runner.prototype.run = function(fn){ 3419 | var self = this 3420 | , fn = fn || function(){}; 3421 | 3422 | debug('start'); 3423 | 3424 | // callback 3425 | this.on('end', function(){ 3426 | debug('end'); 3427 | process.removeListener('uncaughtException', this.uncaught); 3428 | fn(self.failures); 3429 | }); 3430 | 3431 | // run suites 3432 | this.emit('start'); 3433 | this.runSuite(this.suite, function(){ 3434 | debug('finished running'); 3435 | self.emit('end'); 3436 | }); 3437 | 3438 | // uncaught exception 3439 | process.on('uncaughtException', function(err){ 3440 | self.uncaught(err); 3441 | }); 3442 | 3443 | return this; 3444 | }; 3445 | 3446 | }); // module: runner.js 3447 | 3448 | require.register("suite.js", function(module, exports, require){ 3449 | 3450 | /** 3451 | * Module dependencies. 3452 | */ 3453 | 3454 | var EventEmitter = require('browser/events').EventEmitter 3455 | , debug = require('browser/debug')('suite') 3456 | , utils = require('./utils') 3457 | , Hook = require('./hook'); 3458 | 3459 | /** 3460 | * Expose `Suite`. 3461 | */ 3462 | 3463 | exports = module.exports = Suite; 3464 | 3465 | /** 3466 | * Create a new `Suite` with the given `title` 3467 | * and parent `Suite`. When a suite with the 3468 | * same title is already present, that suite 3469 | * is returned to provide nicer reporter 3470 | * and more flexible meta-testing. 3471 | * 3472 | * @param {Suite} parent 3473 | * @param {String} title 3474 | * @return {Suite} 3475 | * @api public 3476 | */ 3477 | 3478 | exports.create = function(parent, title){ 3479 | var suite = new Suite(title, parent.ctx); 3480 | suite.parent = parent; 3481 | title = suite.fullTitle(); 3482 | parent.addSuite(suite); 3483 | return suite; 3484 | }; 3485 | 3486 | /** 3487 | * Initialize a new `Suite` with the given 3488 | * `title` and `ctx`. 3489 | * 3490 | * @param {String} title 3491 | * @param {Context} ctx 3492 | * @api private 3493 | */ 3494 | 3495 | function Suite(title, ctx) { 3496 | this.title = title; 3497 | this.ctx = ctx; 3498 | this.suites = []; 3499 | this.tests = []; 3500 | this._beforeEach = []; 3501 | this._beforeAll = []; 3502 | this._afterEach = []; 3503 | this._afterAll = []; 3504 | this.root = !title; 3505 | this._timeout = 2000; 3506 | this._bail = false; 3507 | } 3508 | 3509 | /** 3510 | * Inherit from `EventEmitter.prototype`. 3511 | */ 3512 | 3513 | Suite.prototype = new EventEmitter; 3514 | Suite.prototype.constructor = Suite; 3515 | 3516 | 3517 | /** 3518 | * Return a clone of this `Suite`. 3519 | * 3520 | * @return {Suite} 3521 | * @api private 3522 | */ 3523 | 3524 | Suite.prototype.clone = function(){ 3525 | var suite = new Suite(this.title); 3526 | debug('clone'); 3527 | suite.ctx = this.ctx; 3528 | suite.timeout(this.timeout()); 3529 | suite.bail(this.bail()); 3530 | return suite; 3531 | }; 3532 | 3533 | /** 3534 | * Set timeout `ms` or short-hand such as "2s". 3535 | * 3536 | * @param {Number|String} ms 3537 | * @return {Suite|Number} for chaining 3538 | * @api private 3539 | */ 3540 | 3541 | Suite.prototype.timeout = function(ms){ 3542 | if (0 == arguments.length) return this._timeout; 3543 | if (String(ms).match(/s$/)) ms = parseFloat(ms) * 1000; 3544 | debug('timeout %d', ms); 3545 | this._timeout = parseInt(ms, 10); 3546 | return this; 3547 | }; 3548 | 3549 | /** 3550 | * Sets whether to bail after first error. 3551 | * 3552 | * @parma {Boolean} bail 3553 | * @return {Suite|Number} for chaining 3554 | * @api private 3555 | */ 3556 | 3557 | Suite.prototype.bail = function(bail){ 3558 | if (0 == arguments.length) return this._bail; 3559 | debug('bail %s', bail); 3560 | this._bail = bail; 3561 | return this; 3562 | }; 3563 | 3564 | /** 3565 | * Run `fn(test[, done])` before running tests. 3566 | * 3567 | * @param {Function} fn 3568 | * @return {Suite} for chaining 3569 | * @api private 3570 | */ 3571 | 3572 | Suite.prototype.beforeAll = function(fn){ 3573 | var hook = new Hook('"before all" hook', fn); 3574 | hook.parent = this; 3575 | hook.timeout(this.timeout()); 3576 | hook.ctx = this.ctx; 3577 | this._beforeAll.push(hook); 3578 | this.emit('beforeAll', hook); 3579 | return this; 3580 | }; 3581 | 3582 | /** 3583 | * Run `fn(test[, done])` after running tests. 3584 | * 3585 | * @param {Function} fn 3586 | * @return {Suite} for chaining 3587 | * @api private 3588 | */ 3589 | 3590 | Suite.prototype.afterAll = function(fn){ 3591 | var hook = new Hook('"after all" hook', fn); 3592 | hook.parent = this; 3593 | hook.timeout(this.timeout()); 3594 | hook.ctx = this.ctx; 3595 | this._afterAll.push(hook); 3596 | this.emit('afterAll', hook); 3597 | return this; 3598 | }; 3599 | 3600 | /** 3601 | * Run `fn(test[, done])` before each test case. 3602 | * 3603 | * @param {Function} fn 3604 | * @return {Suite} for chaining 3605 | * @api private 3606 | */ 3607 | 3608 | Suite.prototype.beforeEach = function(fn){ 3609 | var hook = new Hook('"before each" hook', fn); 3610 | hook.parent = this; 3611 | hook.timeout(this.timeout()); 3612 | hook.ctx = this.ctx; 3613 | this._beforeEach.push(hook); 3614 | this.emit('beforeEach', hook); 3615 | return this; 3616 | }; 3617 | 3618 | /** 3619 | * Run `fn(test[, done])` after each test case. 3620 | * 3621 | * @param {Function} fn 3622 | * @return {Suite} for chaining 3623 | * @api private 3624 | */ 3625 | 3626 | Suite.prototype.afterEach = function(fn){ 3627 | var hook = new Hook('"after each" hook', fn); 3628 | hook.parent = this; 3629 | hook.timeout(this.timeout()); 3630 | hook.ctx = this.ctx; 3631 | this._afterEach.push(hook); 3632 | this.emit('afterEach', hook); 3633 | return this; 3634 | }; 3635 | 3636 | /** 3637 | * Add a test `suite`. 3638 | * 3639 | * @param {Suite} suite 3640 | * @return {Suite} for chaining 3641 | * @api private 3642 | */ 3643 | 3644 | Suite.prototype.addSuite = function(suite){ 3645 | suite.parent = this; 3646 | suite.timeout(this.timeout()); 3647 | suite.bail(this.bail()); 3648 | this.suites.push(suite); 3649 | this.emit('suite', suite); 3650 | return this; 3651 | }; 3652 | 3653 | /** 3654 | * Add a `test` to this suite. 3655 | * 3656 | * @param {Test} test 3657 | * @return {Suite} for chaining 3658 | * @api private 3659 | */ 3660 | 3661 | Suite.prototype.addTest = function(test){ 3662 | test.parent = this; 3663 | test.timeout(this.timeout()); 3664 | test.ctx = this.ctx; 3665 | this.tests.push(test); 3666 | this.emit('test', test); 3667 | return this; 3668 | }; 3669 | 3670 | /** 3671 | * Return the full title generated by recursively 3672 | * concatenating the parent's full title. 3673 | * 3674 | * @return {String} 3675 | * @api public 3676 | */ 3677 | 3678 | Suite.prototype.fullTitle = function(){ 3679 | if (this.parent) { 3680 | var full = this.parent.fullTitle(); 3681 | if (full) return full + ' ' + this.title; 3682 | } 3683 | return this.title; 3684 | }; 3685 | 3686 | /** 3687 | * Return the total number of tests. 3688 | * 3689 | * @return {Number} 3690 | * @api public 3691 | */ 3692 | 3693 | Suite.prototype.total = function(){ 3694 | return utils.reduce(this.suites, function(sum, suite){ 3695 | return sum + suite.total(); 3696 | }, 0) + this.tests.length; 3697 | }; 3698 | 3699 | /** 3700 | * Iterates through each suite recursively to find 3701 | * all tests. Applies a function in the format 3702 | * `fn(test)`. 3703 | * 3704 | * @param {Function} fn 3705 | * @return {Suite} 3706 | * @api private 3707 | */ 3708 | 3709 | Suite.prototype.eachTest = function(fn){ 3710 | utils.forEach(this.tests, fn); 3711 | utils.forEach(this.suites, function(suite){ 3712 | suite.eachTest(fn); 3713 | }); 3714 | return this; 3715 | }; 3716 | 3717 | }); // module: suite.js 3718 | 3719 | require.register("test.js", function(module, exports, require){ 3720 | 3721 | /** 3722 | * Module dependencies. 3723 | */ 3724 | 3725 | var Runnable = require('./runnable'); 3726 | 3727 | /** 3728 | * Expose `Test`. 3729 | */ 3730 | 3731 | module.exports = Test; 3732 | 3733 | /** 3734 | * Initialize a new `Test` with the given `title` and callback `fn`. 3735 | * 3736 | * @param {String} title 3737 | * @param {Function} fn 3738 | * @api private 3739 | */ 3740 | 3741 | function Test(title, fn) { 3742 | Runnable.call(this, title, fn); 3743 | this.pending = !fn; 3744 | this.type = 'test'; 3745 | } 3746 | 3747 | /** 3748 | * Inherit from `Runnable.prototype`. 3749 | */ 3750 | 3751 | Test.prototype = new Runnable; 3752 | Test.prototype.constructor = Test; 3753 | 3754 | 3755 | /** 3756 | * Inspect the context void of private properties. 3757 | * 3758 | * @return {String} 3759 | * @api private 3760 | */ 3761 | 3762 | Test.prototype.inspect = function(){ 3763 | return JSON.stringify(this, function(key, val){ 3764 | return '_' == key[0] 3765 | ? undefined 3766 | : 'parent' == key 3767 | ? '#' 3768 | : val; 3769 | }, 2); 3770 | }; 3771 | }); // module: test.js 3772 | 3773 | require.register("utils.js", function(module, exports, require){ 3774 | 3775 | /** 3776 | * Module dependencies. 3777 | */ 3778 | 3779 | var fs = require('browser/fs') 3780 | , path = require('browser/path') 3781 | , join = path.join 3782 | , debug = require('browser/debug')('watch'); 3783 | 3784 | /** 3785 | * Ignored directories. 3786 | */ 3787 | 3788 | var ignore = ['node_modules', '.git']; 3789 | 3790 | /** 3791 | * Escape special characters in the given string of html. 3792 | * 3793 | * @param {String} html 3794 | * @return {String} 3795 | * @api private 3796 | */ 3797 | 3798 | exports.escape = function(html) { 3799 | return String(html) 3800 | .replace(/&/g, '&') 3801 | .replace(/"/g, '"') 3802 | .replace(//g, '>'); 3804 | }; 3805 | 3806 | /** 3807 | * Array#forEach (<=IE8) 3808 | * 3809 | * @param {Array} array 3810 | * @param {Function} fn 3811 | * @param {Object} scope 3812 | * @api private 3813 | */ 3814 | 3815 | exports.forEach = function(arr, fn, scope) { 3816 | for (var i = 0, l = arr.length; i < l; i++) 3817 | fn.call(scope, arr[i], i); 3818 | }; 3819 | 3820 | /** 3821 | * Array#indexOf (<=IE8) 3822 | * 3823 | * @parma {Array} arr 3824 | * @param {Object} obj to find index of 3825 | * @param {Number} start 3826 | * @api private 3827 | */ 3828 | 3829 | exports.indexOf = function (arr, obj, start) { 3830 | for (var i = start || 0, l = arr.length; i < l; i++) { 3831 | if (arr[i] === obj) 3832 | return i; 3833 | } 3834 | return -1; 3835 | }; 3836 | 3837 | /** 3838 | * Array#reduce (<=IE8) 3839 | * 3840 | * @param {Array} array 3841 | * @param {Function} fn 3842 | * @param {Object} initial value 3843 | * @param {Object} scope 3844 | * @api private 3845 | */ 3846 | 3847 | exports.reduce = function(arr, fn, val, scope) { 3848 | var rval = val; 3849 | 3850 | for (var i = 0, l = arr.length; i < l; i++) { 3851 | rval = fn.call(scope, rval, arr[i], i, arr); 3852 | } 3853 | 3854 | return rval; 3855 | }; 3856 | 3857 | /** 3858 | * Array#filter (<=IE8) 3859 | * 3860 | * @param {Array} array 3861 | * @param {Function} fn 3862 | * @param {Object} scope 3863 | * @api private 3864 | */ 3865 | 3866 | exports.filter = function(arr, fn, scope) { 3867 | var ret = []; 3868 | 3869 | for (var i = 0, l = arr.length; i < l; i++) { 3870 | var val = arr[i]; 3871 | if (fn.call(scope, val, i, arr)) 3872 | ret.push(val); 3873 | } 3874 | 3875 | return ret; 3876 | }; 3877 | 3878 | /** 3879 | * Object.keys (<=IE8) 3880 | * 3881 | * @param {Object} obj 3882 | * @return {Array} keys 3883 | * @api private 3884 | */ 3885 | 3886 | exports.keys = Object.keys || function(obj) { 3887 | var keys = [] 3888 | , has = Object.prototype.hasOwnProperty // for `window` on <=IE8 3889 | 3890 | for (var key in obj) { 3891 | if (has.call(obj, key)) { 3892 | keys.push(key); 3893 | } 3894 | } 3895 | 3896 | return keys; 3897 | }; 3898 | 3899 | /** 3900 | * Watch the given `files` for changes 3901 | * and invoke `fn(file)` on modification. 3902 | * 3903 | * @param {Array} files 3904 | * @param {Function} fn 3905 | * @api private 3906 | */ 3907 | 3908 | exports.watch = function(files, fn){ 3909 | var options = { interval: 100 }; 3910 | files.forEach(function(file){ 3911 | debug('file %s', file); 3912 | fs.watchFile(file, options, function(curr, prev){ 3913 | if (prev.mtime < curr.mtime) fn(file); 3914 | }); 3915 | }); 3916 | }; 3917 | 3918 | /** 3919 | * Ignored files. 3920 | */ 3921 | 3922 | function ignored(path){ 3923 | return !~ignore.indexOf(path); 3924 | } 3925 | 3926 | /** 3927 | * Lookup files in the given `dir`. 3928 | * 3929 | * @return {Array} 3930 | * @api private 3931 | */ 3932 | 3933 | exports.files = function(dir, ret){ 3934 | ret = ret || []; 3935 | 3936 | fs.readdirSync(dir) 3937 | .filter(ignored) 3938 | .forEach(function(path){ 3939 | path = join(dir, path); 3940 | if (fs.statSync(path).isDirectory()) { 3941 | exports.files(path, ret); 3942 | } else if (path.match(/\.(js|coffee)$/)) { 3943 | ret.push(path); 3944 | } 3945 | }); 3946 | 3947 | return ret; 3948 | }; 3949 | 3950 | /** 3951 | * Compute a slug from the given `str`. 3952 | * 3953 | * @param {String} str 3954 | * @return {String} 3955 | */ 3956 | 3957 | exports.slug = function(str){ 3958 | return str 3959 | .toLowerCase() 3960 | .replace(/ +/g, '-') 3961 | .replace(/[^-\w]/g, ''); 3962 | }; 3963 | }); // module: utils.js 3964 | /** 3965 | * Node shims. 3966 | * 3967 | * These are meant only to allow 3968 | * mocha.js to run untouched, not 3969 | * to allow running node code in 3970 | * the browser. 3971 | */ 3972 | 3973 | process = {}; 3974 | process.exit = function(status){}; 3975 | process.stdout = {}; 3976 | global = window; 3977 | 3978 | /** 3979 | * next tick implementation. 3980 | */ 3981 | 3982 | process.nextTick = (function(){ 3983 | // postMessage behaves badly on IE8 3984 | if (window.ActiveXObject || !window.postMessage) { 3985 | return function(fn){ fn() }; 3986 | } 3987 | 3988 | // based on setZeroTimeout by David Baron 3989 | // - http://dbaron.org/log/20100309-faster-timeouts 3990 | var timeouts = [] 3991 | , name = 'mocha-zero-timeout' 3992 | 3993 | return function(fn){ 3994 | timeouts.push(fn); 3995 | window.postMessage(name, '*'); 3996 | window.addEventListener('message', function(e){ 3997 | if (e.source == window && e.data == name) { 3998 | if (e.stopPropagation) e.stopPropagation(); 3999 | if (timeouts.length) timeouts.shift()(); 4000 | } 4001 | }, true); 4002 | } 4003 | })(); 4004 | 4005 | /** 4006 | * Remove uncaughtException listener. 4007 | */ 4008 | 4009 | process.removeListener = function(e){ 4010 | if ('uncaughtException' == e) { 4011 | window.onerror = null; 4012 | } 4013 | }; 4014 | 4015 | /** 4016 | * Implements uncaughtException listener. 4017 | */ 4018 | 4019 | process.on = function(e, fn){ 4020 | if ('uncaughtException' == e) { 4021 | window.onerror = fn; 4022 | } 4023 | }; 4024 | 4025 | /** 4026 | * Expose mocha. 4027 | */ 4028 | 4029 | window.mocha = require('mocha'); 4030 | 4031 | // boot 4032 | ;(function(){ 4033 | var suite = new mocha.Suite('', new mocha.Context) 4034 | , utils = mocha.utils 4035 | , options = {} 4036 | 4037 | /** 4038 | * Highlight the given string of `js`. 4039 | */ 4040 | 4041 | function highlight(js) { 4042 | return js 4043 | .replace(//g, '>') 4045 | .replace(/\/\/(.*)/gm, '//$1') 4046 | .replace(/('.*?')/gm, '$1') 4047 | .replace(/(\d+\.\d+)/gm, '$1') 4048 | .replace(/(\d+)/gm, '$1') 4049 | .replace(/\bnew *(\w+)/gm, 'new $1') 4050 | .replace(/\b(function|new|throw|return|var|if|else)\b/gm, '$1') 4051 | } 4052 | 4053 | /** 4054 | * Highlight code contents. 4055 | */ 4056 | 4057 | function highlightCode() { 4058 | var code = document.getElementsByTagName('code'); 4059 | for (var i = 0, len = code.length; i < len; ++i) { 4060 | code[i].innerHTML = highlight(code[i].innerHTML); 4061 | } 4062 | } 4063 | 4064 | /** 4065 | * Parse the given `qs`. 4066 | */ 4067 | 4068 | function parse(qs) { 4069 | return utils.reduce(qs.replace('?', '').split('&'), function(obj, pair){ 4070 | var i = pair.indexOf('=') 4071 | , key = pair.slice(0, i) 4072 | , val = pair.slice(++i); 4073 | 4074 | obj[key] = decodeURIComponent(val); 4075 | return obj; 4076 | }, {}); 4077 | } 4078 | 4079 | /** 4080 | * Setup mocha with the given setting options. 4081 | */ 4082 | 4083 | mocha.setup = function(opts){ 4084 | if ('string' === typeof opts) options.ui = opts; 4085 | else options = opts; 4086 | 4087 | ui = mocha.interfaces[options.ui]; 4088 | if (!ui) throw new Error('invalid mocha interface "' + ui + '"'); 4089 | if (options.timeout) suite.timeout(options.timeout); 4090 | ui(suite); 4091 | suite.emit('pre-require', window); 4092 | }; 4093 | 4094 | /** 4095 | * Run mocha, returning the Runner. 4096 | */ 4097 | 4098 | mocha.run = function(fn){ 4099 | suite.emit('run'); 4100 | var runner = new mocha.Runner(suite); 4101 | var Reporter = options.reporter || mocha.reporters.HTML; 4102 | var reporter = new Reporter(runner); 4103 | var query = parse(window.location.search || ""); 4104 | if (query.grep) runner.grep(new RegExp(query.grep)); 4105 | if (options.ignoreLeaks) runner.ignoreLeaks = true; 4106 | if (options.globals) runner.globals(options.globals); 4107 | runner.globals(['location']); 4108 | runner.on('end', highlightCode); 4109 | return runner.run(fn); 4110 | }; 4111 | })(); 4112 | })(); --------------------------------------------------------------------------------