├── .gitignore ├── History.md ├── Makefile ├── Readme.md ├── bin └── marc ├── component.json ├── index.js ├── package.json └── test ├── api.js ├── browser ├── index.html ├── marc.js └── mocha │ ├── mocha.css │ └── mocha.js ├── markdown.js ├── markdown ├── amy.html └── basic.md ├── options.js └── partials.js /.gitignore: -------------------------------------------------------------------------------- 1 | components 2 | node_modules 3 | build 4 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bredele/marc/3e24466bafafaca44bc786354fd36650c3828329/History.md -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | build: components index.js 3 | @component build --dev 4 | 5 | components: component.json 6 | @component install --dev 7 | 8 | clean: 9 | rm -fr build components template.js 10 | 11 | .PHONY: clean 12 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # marc 2 | 3 | > Markdown as a dynamic template engine 4 | 5 | 6 | Markdown is a text-to-HTML conversion tool for web writers. With a plain easy-to-write text you can create a website, a blog, a todo list, a slideshow or even a project presentation (like I've done here on github). However it is mostly used to generate static content. 7 | 8 | `marc` makes markdown a dynamic conversion engine with integrated **templates** that **updates automatically** when the underlying data changes. It also allows you to create **filters** or **partials** and makes easy to create content dynamically from a database. 9 | 10 | ## Installation 11 | 12 | `marc` works on both client and server side: 13 | 14 | with [component](http://component.io): 15 | 16 | $ component install bredele/marc 17 | 18 | with [nodejs](http://nodejs.org): 19 | 20 | $ npm install marc 21 | 22 | 23 | ### Command line 24 | 25 | Install globally: 26 | 27 | $ npm install -g marc 28 | 29 | ``` 30 | Usage: marc [] [] 31 | 32 | Examples: 33 | 34 | # pass an input and output file: 35 | $ marc input.md output.html 36 | 37 | ``` 38 | 39 | ## Usage 40 | 41 | `marc` can be initialized with an optional data. 42 | 43 | ```js 44 | var marc = require('marc')(data); 45 | ``` 46 | 47 | ### Basic 48 | 49 | `marc` generates html from markdown. 50 | 51 | ```js 52 | marc('I am using __markdown__.'); 53 | //I am using markdown. 54 | ``` 55 | 56 | ### Templating 57 | 58 | `marc` is also a template engine! It makes markdown less static and allows you to substitute variables (double brackets `{{}}` with any data. 59 | 60 | ```js 61 | marc.set('label', 'marc!'); 62 | marc('I am using __markdown__ with {{label}}.', true); 63 | //I am using markdown with marc!. 64 | ``` 65 | 66 | ### Dynamic 67 | 68 | `marc` makes your markdown dynamic! It updates automatically when the underlying data changes. 69 | 70 | ```js 71 | marc('I am using __narkdown__ with {{label}}.', function(val) { 72 | //I am using markdown with marc!. 73 | //I am using markdown with github. 74 | }); 75 | 76 | marc.set('label', 'marc!'); 77 | marc.set('label', 'github'); 78 | ``` 79 | 80 | ## Features 81 | 82 | ### Partials 83 | 84 | `marc` allows you to use partials (`{> name }`) 85 | 86 | 87 | ```js 88 | marc.partial('hello', '__{{ label }}__!'); 89 | marc('This is a partial: {> hello }.', function(val){ 90 | //This is a partial: hello world! 91 | }); 92 | 93 | marc.set('label', 'hello world'); 94 | ``` 95 | 96 | ### Filters 97 | 98 | `marc` allows you to apply filter(s) to your markdown in a unix-like fashion. 99 | 100 | ```js 101 | marc.filter('hello', function(str) { 102 | return 'hello ' + str + '!'; 103 | }); 104 | marc('# {{ name } | hello}.', true); 105 | //hello world!. 106 | ``` 107 | 108 | > filters can be chained and reused multiple times. 109 | 110 | 121 | 122 | ### Config 123 | 124 | `marc` use [marked](https://github.com/chjj/marked) and allows to set markdown options as following: 125 | 126 | ```js 127 | //single options 128 | marc.config('sanitize', true); 129 | 130 | //multiple options 131 | marc.config({ 132 | gfm:true, 133 | smartypants:true 134 | }) 135 | ``` 136 | 137 | or get options: 138 | 139 | ```js 140 | marc.config('sanitize'); 141 | ``` 142 | 143 | 144 | ## API 145 | 146 | ### General 147 | 148 | #### marc(str, fn) 149 | 150 | Generate HTML from markdown. 151 | 152 | ```js 153 | marc('I am using __markdown__.'); 154 | marc('hello __markdown__ with {{label}}', true); 155 | marc('hello __markdown__ with {{label}}', function(val) { 156 | //do something on change 157 | }); 158 | ``` 159 | 160 | Second argument is optional (substitute template variables if truethy). 161 | 162 | 163 | #### marc.filter(str, fn) 164 | 165 | Add template filter. 166 | 167 | ```js 168 | marc.filter('hello', function(str) { 169 | return 'hello ' + str + '!'; 170 | }); 171 | marc('# {{ label } | hello}', true); 172 | 173 | ``` 174 | 175 | Second argument is a function and takes the template variable as argument. 176 | 177 | #### marc.partial(name, str) 178 | 179 | Add partial. 180 | 181 | ```js 182 | marc.partial('hello','__{{ name }}__'); 183 | marc('hello {> hello }', true); 184 | ``` 185 | 186 | #### marc.config(name, val) 187 | 188 | Set markdown options. 189 | 190 | ```js 191 | marc.config('sanitize','true'); 192 | marc.config({ 193 | sanitize: false, 194 | gfm: true 195 | }); 196 | ``` 197 | 198 | or get options: 199 | 200 | 201 | ```js 202 | marc.config('sanitize'); 203 | ``` 204 | 205 | ### Datastore 206 | 207 | `marc` is basically a mixin of [datastore](http://github.com/bredele/datastore) and exposes its entire api through the option `marc.data`. Here's an example of computed property: 208 | 209 | ```js 210 | marc.data.compute('name',function() { 211 | return this.firstName + ' ' + this.lastName; 212 | }); 213 | ``` 214 | 215 | However, `marc` overrides some of the most used store handler such as `get` and `set` just for the beauty of code. 216 | 217 | #### .set(name, data) 218 | 219 | Set an attribute `name` with data object. 220 | 221 | object store: 222 | ```js 223 | marc.set('nickname','bredele'); 224 | ``` 225 | 226 | Or update data: 227 | 228 | ```js 229 | marc.set({ 230 | nickname: 'olivier', 231 | lastname: 'wietrich' 232 | }); 233 | ``` 234 | 235 | #### .get(name) 236 | 237 | Get an attribute `name`. 238 | 239 | ```js 240 | marc.get('nickname'); 241 | ``` 242 | 243 | ## License 244 | 245 | The MIT License (MIT) 246 | 247 | Copyright (c) 2014 248 | 249 | Permission is hereby granted, free of charge, to any person obtaining a copy 250 | of this software and associated documentation files (the "Software"), to deal 251 | in the Software without restriction, including without limitation the rights 252 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 253 | copies of the Software, and to permit persons to whom the Software is 254 | furnished to do so, subject to the following conditions: 255 | 256 | The above copyright notice and this permission notice shall be included in 257 | all copies or substantial portions of the Software. 258 | 259 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 260 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 261 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 262 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 263 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 264 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 265 | THE SOFTWARE. 266 | -------------------------------------------------------------------------------- /bin/marc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var program = require('commander'), 4 | path = require('path'), 5 | resolve = path.resolve, 6 | read = require('read-file-stdin'), 7 | write = require('write-file-stdout'), 8 | marc = require('..')(); 9 | 10 | /** 11 | * Usage. 12 | */ 13 | 14 | program 15 | .version(require('../package.json').version) 16 | .usage('[] []'); 17 | 18 | 19 | /** 20 | * Examples. 21 | */ 22 | 23 | program.on('--help', function () { 24 | console.log(' Examples:'); 25 | console.log(); 26 | console.log(' # pass an input and output file:'); 27 | console.log(' $ marc input.md output.html'); 28 | console.log(); 29 | }); 30 | 31 | 32 | /** 33 | * Parse. 34 | */ 35 | 36 | program.parse(process.argv); 37 | 38 | var input = program.args[0] ? resolve(program.args[0]) : null; 39 | var output = program.args[1] ? resolve(program.args[1]) : null; 40 | 41 | read(input, function (err, buffer) { 42 | if (err) throw(err); 43 | write(output, marc(buffer.toString())); 44 | }); -------------------------------------------------------------------------------- /component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "marc", 3 | "repo": "bredele/marc", 4 | "description": "A better markdown parser and compiler", 5 | "version": "0.1.0", 6 | "keywords": [], 7 | "dependencies": { 8 | "bredele/datastore": "*", 9 | "chjj/marked": "*", 10 | "component/trim" : "*", 11 | "bredele/store-supplant": "*" 12 | }, 13 | "development": { 14 | "component/assert" : "*" 15 | }, 16 | "license": "MIT", 17 | "main": "index.js", 18 | "scripts": [ 19 | "index.js" 20 | ] 21 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var supplant = require('store-supplant'); 2 | var marked = require('marked'); 3 | var trim = require('trim'); 4 | var Store = require('datastore'); 5 | 6 | 7 | /** 8 | * Expose 'marc' 9 | */ 10 | 11 | module.exports = function(data) { 12 | 13 | 14 | /** 15 | * Partials. 16 | * @type {Object} 17 | */ 18 | 19 | var partials = {}; 20 | 21 | 22 | /** 23 | * Data 24 | * @type {Store} 25 | */ 26 | 27 | var store = marc.data = new Store(data); 28 | store.use(supplant); 29 | 30 | 31 | /** 32 | * Replace partial. 33 | * 34 | * @api private 35 | */ 36 | 37 | function partial(str, fn) { 38 | return str.replace(/\{>([^}]+)\}/g, function(_, expr) { 39 | var name = trim(expr), 40 | val = partials[name]; 41 | if(val){ 42 | val = marc(val); 43 | if(val.substring(0, 3) === '') val = val.substring(3, val.length - 5); 44 | return val; 45 | } 46 | }); 47 | } 48 | 49 | 50 | /** 51 | * Generates html from mardown. 52 | * If the second argument is truethy, marc will substitute 53 | * the markdown variables with data. 54 | * example: 55 | * 56 | * marc('hello __world__'); 57 | * marc('hello __{{ label }}__', true); 58 | * marc('hello __{{ label }}__', function(val) { 59 | * //do something 60 | * }); 61 | * 62 | * @param {String} markdown 63 | * @param {Boolean|Function} fn 64 | * @return {String} escaped html 65 | * @api public 66 | */ 67 | 68 | function marc(str, fn) { 69 | var result = marked(str); 70 | if(fn) { 71 | result = store.supplant(partial(result), typeof fn === 'function' ? fn : false); 72 | } 73 | return result; 74 | } 75 | 76 | 77 | /** 78 | * Set data. 79 | * @see http://github.com/bredele/store 80 | * 81 | * @param {String|Object} name n] 82 | * @param {Any} val 83 | * @api public 84 | */ 85 | 86 | marc.set = function(name, val) { 87 | store.set(name, val); 88 | return this; 89 | }; 90 | 91 | 92 | /** 93 | * Get data. 94 | * @see http://github.com/bredele/store 95 | * 96 | * @param {String} name n] 97 | * @return {Any} val 98 | * @api public 99 | */ 100 | 101 | marc.get = function(name) { 102 | return store.get(name); 103 | }; 104 | 105 | 106 | /** 107 | * Add template filters. 108 | * example: 109 | * 110 | * marc.filter('hello', function(str) { 111 | * return 'hello ' + str + '!'; 112 | * }); 113 | * 114 | * @param {String} name 115 | * @param {Function} fn 116 | * @api public 117 | */ 118 | 119 | marc.filter = function(name, fn) { 120 | store.filter(name, fn); 121 | return this; 122 | }; 123 | 124 | 125 | /** 126 | * Set/get markdown options. 127 | * example: 128 | * 129 | * //set 130 | * marc.config('sanitize', true); 131 | * marc.config({ 132 | * sanitize: true, 133 | * gfm: true 134 | * }); 135 | * //get 136 | * marc.config('sanitize'); 137 | * 138 | * @param {String|Object} name 139 | * @param {Any} val 140 | * @api public 141 | */ 142 | 143 | marc.config = function(name, val) { 144 | if(typeof name === 'string') { 145 | if(val) { 146 | var obj = {}; 147 | obj[name] = val; 148 | marked.setOptions(obj); 149 | } else { 150 | return marked.defaults[name]; 151 | } 152 | } else { 153 | marked.setOptions(name); 154 | } 155 | return this; 156 | }; 157 | 158 | 159 | /** 160 | * Add template partial. 161 | * A partial is called everytime there is {> } 162 | * example: 163 | * 164 | * marc.partial('hello', 'hello __{{ label }}__'); 165 | * 166 | * @param {String} name 167 | * @param {String} str 168 | * @api public 169 | */ 170 | 171 | marc.partial = function(name, str) { 172 | partials[name] = str; 173 | }; 174 | 175 | 176 | 177 | return marc; 178 | 179 | }; 180 | 181 | 182 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "marc", 3 | "version": "0.1.0", 4 | "description": "A better markdown parser and compiler", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha" 8 | }, 9 | "bin": { 10 | "marc": "bin/marc" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git://github.com/bredele/marc.git" 15 | }, 16 | "keywords": [ 17 | "markdown", 18 | "parser", 19 | "compiler" 20 | ], 21 | "author": "Olivier Wietrich (http://github.com/bredele)", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/bredele/marc/issues" 25 | }, 26 | "devDependencies": { 27 | "mocha": "~1.17.1" 28 | }, 29 | "dependencies": { 30 | "commander": "~2.1.0", 31 | "datastore": "^1.0.0", 32 | "marked": "~0.3.1", 33 | "read-file-stdin": "0.0.3", 34 | "store-supplant": "0.0.2", 35 | "trim": "0.0.1", 36 | "write-file-stdout": "0.0.2" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/api.js: -------------------------------------------------------------------------------- 1 | var constructor = require('..'), 2 | assert = require('assert'); 3 | 4 | 5 | describe("API", function() { 6 | 7 | describe("constructor", function() { 8 | 9 | it("should be a function", function() { 10 | assert.equal(typeof constructor, 'function'); 11 | }); 12 | 13 | it('should return a function', function() { 14 | assert.equal(typeof constructor(), 'function'); 15 | }); 16 | 17 | }); 18 | 19 | describe("inherits from store", function() { 20 | var marc; 21 | beforeEach(function() { 22 | marc = constructor(); 23 | }); 24 | 25 | it('should mixin store api', function() { 26 | assert.equal(typeof marc.set, 'function'); 27 | assert.equal(typeof marc.get, 'function'); 28 | }); 29 | 30 | it("should set/get data", function() { 31 | marc.set('template', 'marc'); 32 | assert.equal(marc.get('template'), 'marc'); 33 | }); 34 | 35 | }); 36 | 37 | 38 | }); 39 | -------------------------------------------------------------------------------- /test/browser/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Marc 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/browser/marc.js: -------------------------------------------------------------------------------- 1 | var constructor = require('marc'), 2 | assert = require('assert'); 3 | 4 | 5 | describe("API", function() { 6 | 7 | describe("constructor", function() { 8 | 9 | it("should be a function", function() { 10 | assert.equal(typeof constructor, 'function'); 11 | }); 12 | 13 | it('should return a function', function() { 14 | assert.equal(typeof constructor(), 'function'); 15 | }); 16 | 17 | }); 18 | 19 | describe("inherits from store", function() { 20 | var marc; 21 | beforeEach(function() { 22 | marc = constructor(); 23 | }); 24 | 25 | it('should mixin store api', function() { 26 | assert.equal(typeof marc.set, 'function'); 27 | assert.equal(typeof marc.get, 'function'); 28 | }); 29 | 30 | it("should set/get data", function() { 31 | marc.set('template', 'marc'); 32 | assert.equal(marc.get('template'), 'marc'); 33 | }); 34 | 35 | }); 36 | 37 | }); 38 | 39 | 40 | describe("Markdown", function() { 41 | 42 | describe("Basic", function() { 43 | var marc; 44 | beforeEach(function() { 45 | marc = constructor(); 46 | }); 47 | 48 | it('should render html', function() { 49 | var result = marc('I am using __markdown__.'); 50 | assert.equal(result,'I am using markdown.\n'); 51 | }); 52 | 53 | }); 54 | 55 | describe("Dynamic", function() { 56 | var marc; 57 | beforeEach(function() { 58 | marc = constructor({ 59 | label: 'markdown' 60 | }); 61 | }); 62 | 63 | it('should render html with data', function() { 64 | var result = marc('I am using __{{label}}__.', true); 65 | assert.equal(result,'I am using markdown.\n'); 66 | }); 67 | 68 | it('should update rendered html', function(done) { 69 | var expected = 'I am using other.\n'; 70 | var result = marc('I am using __{{label}}__.', function(val) { 71 | if(val === expected) done(); 72 | }); 73 | assert.equal(result, 'I am using markdown.\n'); 74 | marc.set('label', 'other'); 75 | }); 76 | 77 | it('should update html everytime data changes', function(done) { 78 | var i = 0, 79 | expected = 'I am using bredele.\n'; 80 | var result = marc('I am using __{{label}}__.', function(val) { 81 | i++; 82 | if(i === 2 && val === expected) done(); 83 | }); 84 | marc.set('label', 'other'); 85 | marc.set('label', 'bredele'); 86 | }); 87 | 88 | it('should filter markdown', function() { 89 | marc.filter('upper', function(str) { 90 | return str.toUpperCase(); 91 | }); 92 | var result = marc('I am using __{{label} | upper}__.', true); 93 | assert.equal(result,'I am using MARKDOWN.\n'); 94 | }); 95 | 96 | // console.log(marc('__{{>partial}}__.')); 97 | 98 | }); 99 | 100 | }); 101 | 102 | describe("Options", function() { 103 | 104 | var marc; 105 | beforeEach(function() { 106 | marc = constructor(); 107 | }); 108 | 109 | it("should have a options handler", function() { 110 | assert.equal(typeof marc.config, 'function'); 111 | }); 112 | 113 | 114 | it("should set and get option", function() { 115 | marc.config('sanitize', true); 116 | assert.equal(marc.config('sanitize'), true); 117 | }); 118 | 119 | it('should set multiple options', function() { 120 | marc.config({ 121 | sanitize: true, 122 | escaped:false 123 | }); 124 | assert.equal(marc.config('sanitize'), true); 125 | assert.equal(marc.config('escaped'), false); 126 | }); 127 | 128 | }); 129 | 130 | 131 | describe("Partials", function() { 132 | var marc; 133 | beforeEach(function() { 134 | marc = constructor(); 135 | }); 136 | 137 | it("should add partials markdown", function() { 138 | assert.equal(typeof marc.partial, 'function'); 139 | }); 140 | 141 | describe("render:", function() { 142 | 143 | it("should render partials", function() { 144 | marc.partial('hello', '__hello world!__'); 145 | var result = marc('This is a partial {> hello }', true); 146 | assert.equal(result, 'This is a partial hello world!\n'); 147 | }); 148 | 149 | it('should render partials with data', function() { 150 | marc.partial('hello', '__{{label}}!__'); 151 | marc.set({ 152 | label: 'hello world', 153 | name: 'hello' 154 | }); 155 | var result = marc('This is the partial {{ name }}:{> hello }', true); 156 | assert.equal(result, 'This is the partial hello:hello world!\n'); 157 | }); 158 | 159 | it('should update partials on changes', function(done) { 160 | marc.partial('hello', '__{{label}}!__'); 161 | var result = marc('This is a partial {> hello }', function(val) { 162 | if(val === 'This is a partial hello world!\n') done(); 163 | }); 164 | marc.set('label', 'hello world'); 165 | }); 166 | 167 | it('should update partials on changes', function(done) { 168 | marc.partial('hello', '__{{name}}!__'); 169 | marc.set('name', 'hello world'); 170 | var result = marc('This is a {{label}} {> hello }', function(val) { 171 | if(val === 'This is a partial hello world!\n') done(); 172 | }); 173 | marc.set('label', 'partial'); 174 | }); 175 | 176 | }); 177 | 178 | 179 | }); 180 | 181 | 182 | -------------------------------------------------------------------------------- /test/browser/mocha/mocha.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | body { 4 | font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; 5 | padding: 60px 50px; 6 | } 7 | 8 | #mocha ul, #mocha li { 9 | margin: 0; 10 | padding: 0; 11 | } 12 | 13 | #mocha ul { 14 | list-style: none; 15 | } 16 | 17 | #mocha h1, #mocha h2 { 18 | margin: 0; 19 | } 20 | 21 | #mocha h1 { 22 | margin-top: 15px; 23 | font-size: 1em; 24 | font-weight: 200; 25 | } 26 | 27 | #mocha h1 a { 28 | text-decoration: none; 29 | color: inherit; 30 | } 31 | 32 | #mocha h1 a:hover { 33 | text-decoration: underline; 34 | } 35 | 36 | #mocha .suite .suite h1 { 37 | margin-top: 0; 38 | font-size: .8em; 39 | } 40 | 41 | .hidden { 42 | display: none; 43 | } 44 | 45 | #mocha h2 { 46 | font-size: 12px; 47 | font-weight: normal; 48 | cursor: pointer; 49 | } 50 | 51 | #mocha .suite { 52 | margin-left: 15px; 53 | } 54 | 55 | #mocha .test { 56 | margin-left: 15px; 57 | overflow: hidden; 58 | } 59 | 60 | #mocha .test.pending:hover h2::after { 61 | content: '(pending)'; 62 | font-family: arial; 63 | } 64 | 65 | #mocha .test.pass.medium .duration { 66 | background: #C09853; 67 | } 68 | 69 | #mocha .test.pass.slow .duration { 70 | background: #B94A48; 71 | } 72 | 73 | #mocha .test.pass::before { 74 | content: '✓'; 75 | font-size: 12px; 76 | display: block; 77 | float: left; 78 | margin-right: 5px; 79 | color: #00d6b2; 80 | } 81 | 82 | #mocha .test.pass .duration { 83 | font-size: 9px; 84 | margin-left: 5px; 85 | padding: 2px 5px; 86 | color: white; 87 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 88 | -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 89 | box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 90 | -webkit-border-radius: 5px; 91 | -moz-border-radius: 5px; 92 | -ms-border-radius: 5px; 93 | -o-border-radius: 5px; 94 | border-radius: 5px; 95 | } 96 | 97 | #mocha .test.pass.fast .duration { 98 | display: none; 99 | } 100 | 101 | #mocha .test.pending { 102 | color: #0b97c4; 103 | } 104 | 105 | #mocha .test.pending::before { 106 | content: '◦'; 107 | color: #0b97c4; 108 | } 109 | 110 | #mocha .test.fail { 111 | color: #c00; 112 | } 113 | 114 | #mocha .test.fail pre { 115 | color: black; 116 | } 117 | 118 | #mocha .test.fail::before { 119 | content: '✖'; 120 | font-size: 12px; 121 | display: block; 122 | float: left; 123 | margin-right: 5px; 124 | color: #c00; 125 | } 126 | 127 | #mocha .test pre.error { 128 | color: #c00; 129 | max-height: 300px; 130 | overflow: auto; 131 | } 132 | 133 | #mocha .test pre { 134 | display: block; 135 | float: left; 136 | clear: left; 137 | font: 12px/1.5 monaco, monospace; 138 | margin: 5px; 139 | padding: 15px; 140 | border: 1px solid #eee; 141 | border-bottom-color: #ddd; 142 | -webkit-border-radius: 3px; 143 | -webkit-box-shadow: 0 1px 3px #eee; 144 | -moz-border-radius: 3px; 145 | -moz-box-shadow: 0 1px 3px #eee; 146 | } 147 | 148 | #mocha .test h2 { 149 | position: relative; 150 | } 151 | 152 | #mocha .test a.replay { 153 | position: absolute; 154 | top: 3px; 155 | right: 0; 156 | text-decoration: none; 157 | vertical-align: middle; 158 | display: block; 159 | width: 15px; 160 | height: 15px; 161 | line-height: 15px; 162 | text-align: center; 163 | background: #eee; 164 | font-size: 15px; 165 | -moz-border-radius: 15px; 166 | border-radius: 15px; 167 | -webkit-transition: opacity 200ms; 168 | -moz-transition: opacity 200ms; 169 | transition: opacity 200ms; 170 | opacity: 0.3; 171 | color: #888; 172 | } 173 | 174 | #mocha .test:hover a.replay { 175 | opacity: 1; 176 | } 177 | 178 | #mocha-report.pass .test.fail { 179 | display: none; 180 | } 181 | 182 | #mocha-report.fail .test.pass { 183 | display: none; 184 | } 185 | 186 | #mocha-error { 187 | color: #c00; 188 | font-size: 1.5 em; 189 | font-weight: 100; 190 | letter-spacing: 1px; 191 | } 192 | 193 | #mocha-stats { 194 | position: fixed; 195 | top: 15px; 196 | right: 10px; 197 | font-size: 12px; 198 | margin: 0; 199 | color: #888; 200 | } 201 | 202 | #mocha-stats .progress { 203 | float: right; 204 | padding-top: 0; 205 | } 206 | 207 | #mocha-stats em { 208 | color: black; 209 | } 210 | 211 | #mocha-stats a { 212 | text-decoration: none; 213 | color: inherit; 214 | } 215 | 216 | #mocha-stats a:hover { 217 | border-bottom: 1px solid #eee; 218 | } 219 | 220 | #mocha-stats li { 221 | display: inline-block; 222 | margin: 0 5px; 223 | list-style: none; 224 | padding-top: 11px; 225 | } 226 | 227 | #mocha-stats canvas { 228 | width: 40px; 229 | height: 40px; 230 | } 231 | 232 | code .comment { color: #ddd } 233 | code .init { color: #2F6FAD } 234 | code .string { color: #5890AD } 235 | code .keyword { color: #8A6343 } 236 | code .number { color: #2F6FAD } 237 | 238 | @media screen and (max-device-width: 480px) { 239 | body { 240 | padding: 60px 0px; 241 | } 242 | 243 | #stats { 244 | position: absolute; 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /test/browser/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 public 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 or get 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 | if (0 == arguments.length) return this._runnable; 419 | this.test = this._runnable = runnable; 420 | return this; 421 | }; 422 | 423 | /** 424 | * Set test timeout `ms`. 425 | * 426 | * @param {Number} ms 427 | * @return {Context} self 428 | * @api private 429 | */ 430 | 431 | Context.prototype.timeout = function(ms){ 432 | this.runnable().timeout(ms); 433 | return this; 434 | }; 435 | 436 | /** 437 | * Set test slowness threshold `ms`. 438 | * 439 | * @param {Number} ms 440 | * @return {Context} self 441 | * @api private 442 | */ 443 | 444 | Context.prototype.slow = function(ms){ 445 | this.runnable().slow(ms); 446 | return this; 447 | }; 448 | 449 | /** 450 | * Inspect the context void of `._runnable`. 451 | * 452 | * @return {String} 453 | * @api private 454 | */ 455 | 456 | Context.prototype.inspect = function(){ 457 | return JSON.stringify(this, function(key, val){ 458 | if ('_runnable' == key) return; 459 | if ('test' == key) return; 460 | return val; 461 | }, 2); 462 | }; 463 | 464 | }); // module: context.js 465 | 466 | require.register("hook.js", function(module, exports, require){ 467 | 468 | /** 469 | * Module dependencies. 470 | */ 471 | 472 | var Runnable = require('./runnable'); 473 | 474 | /** 475 | * Expose `Hook`. 476 | */ 477 | 478 | module.exports = Hook; 479 | 480 | /** 481 | * Initialize a new `Hook` with the given `title` and callback `fn`. 482 | * 483 | * @param {String} title 484 | * @param {Function} fn 485 | * @api private 486 | */ 487 | 488 | function Hook(title, fn) { 489 | Runnable.call(this, title, fn); 490 | this.type = 'hook'; 491 | } 492 | 493 | /** 494 | * Inherit from `Runnable.prototype`. 495 | */ 496 | 497 | Hook.prototype = new Runnable; 498 | Hook.prototype.constructor = Hook; 499 | 500 | 501 | /** 502 | * Get or set the test `err`. 503 | * 504 | * @param {Error} err 505 | * @return {Error} 506 | * @api public 507 | */ 508 | 509 | Hook.prototype.error = function(err){ 510 | if (0 == arguments.length) { 511 | var err = this._error; 512 | this._error = null; 513 | return err; 514 | } 515 | 516 | this._error = err; 517 | }; 518 | 519 | 520 | }); // module: hook.js 521 | 522 | require.register("interfaces/bdd.js", function(module, exports, require){ 523 | 524 | /** 525 | * Module dependencies. 526 | */ 527 | 528 | var Suite = require('../suite') 529 | , Test = require('../test'); 530 | 531 | /** 532 | * BDD-style interface: 533 | * 534 | * describe('Array', function(){ 535 | * describe('#indexOf()', function(){ 536 | * it('should return -1 when not present', function(){ 537 | * 538 | * }); 539 | * 540 | * it('should return the index when present', function(){ 541 | * 542 | * }); 543 | * }); 544 | * }); 545 | * 546 | */ 547 | 548 | module.exports = function(suite){ 549 | var suites = [suite]; 550 | 551 | suite.on('pre-require', function(context, file, mocha){ 552 | 553 | /** 554 | * Execute before running tests. 555 | */ 556 | 557 | context.before = function(fn){ 558 | suites[0].beforeAll(fn); 559 | }; 560 | 561 | /** 562 | * Execute after running tests. 563 | */ 564 | 565 | context.after = function(fn){ 566 | suites[0].afterAll(fn); 567 | }; 568 | 569 | /** 570 | * Execute before each test case. 571 | */ 572 | 573 | context.beforeEach = function(fn){ 574 | suites[0].beforeEach(fn); 575 | }; 576 | 577 | /** 578 | * Execute after each test case. 579 | */ 580 | 581 | context.afterEach = function(fn){ 582 | suites[0].afterEach(fn); 583 | }; 584 | 585 | /** 586 | * Describe a "suite" with the given `title` 587 | * and callback `fn` containing nested suites 588 | * and/or tests. 589 | */ 590 | 591 | context.describe = context.context = function(title, fn){ 592 | var suite = Suite.create(suites[0], title); 593 | suites.unshift(suite); 594 | fn.call(suite); 595 | suites.shift(); 596 | return suite; 597 | }; 598 | 599 | /** 600 | * Pending describe. 601 | */ 602 | 603 | context.xdescribe = 604 | context.xcontext = 605 | context.describe.skip = function(title, fn){ 606 | var suite = Suite.create(suites[0], title); 607 | suite.pending = true; 608 | suites.unshift(suite); 609 | fn.call(suite); 610 | suites.shift(); 611 | }; 612 | 613 | /** 614 | * Exclusive suite. 615 | */ 616 | 617 | context.describe.only = function(title, fn){ 618 | var suite = context.describe(title, fn); 619 | mocha.grep(suite.fullTitle()); 620 | }; 621 | 622 | /** 623 | * Describe a specification or test-case 624 | * with the given `title` and callback `fn` 625 | * acting as a thunk. 626 | */ 627 | 628 | context.it = context.specify = function(title, fn){ 629 | var suite = suites[0]; 630 | if (suite.pending) var fn = null; 631 | var test = new Test(title, fn); 632 | suite.addTest(test); 633 | return test; 634 | }; 635 | 636 | /** 637 | * Exclusive test-case. 638 | */ 639 | 640 | context.it.only = function(title, fn){ 641 | var test = context.it(title, fn); 642 | mocha.grep(test.fullTitle()); 643 | }; 644 | 645 | /** 646 | * Pending test case. 647 | */ 648 | 649 | context.xit = 650 | context.xspecify = 651 | context.it.skip = function(title){ 652 | context.it(title); 653 | }; 654 | }); 655 | }; 656 | 657 | }); // module: interfaces/bdd.js 658 | 659 | require.register("interfaces/exports.js", function(module, exports, require){ 660 | 661 | /** 662 | * Module dependencies. 663 | */ 664 | 665 | var Suite = require('../suite') 666 | , Test = require('../test'); 667 | 668 | /** 669 | * TDD-style interface: 670 | * 671 | * exports.Array = { 672 | * '#indexOf()': { 673 | * 'should return -1 when the value is not present': function(){ 674 | * 675 | * }, 676 | * 677 | * 'should return the correct index when the value is present': function(){ 678 | * 679 | * } 680 | * } 681 | * }; 682 | * 683 | */ 684 | 685 | module.exports = function(suite){ 686 | var suites = [suite]; 687 | 688 | suite.on('require', visit); 689 | 690 | function visit(obj) { 691 | var suite; 692 | for (var key in obj) { 693 | if ('function' == typeof obj[key]) { 694 | var fn = obj[key]; 695 | switch (key) { 696 | case 'before': 697 | suites[0].beforeAll(fn); 698 | break; 699 | case 'after': 700 | suites[0].afterAll(fn); 701 | break; 702 | case 'beforeEach': 703 | suites[0].beforeEach(fn); 704 | break; 705 | case 'afterEach': 706 | suites[0].afterEach(fn); 707 | break; 708 | default: 709 | suites[0].addTest(new Test(key, fn)); 710 | } 711 | } else { 712 | var suite = Suite.create(suites[0], key); 713 | suites.unshift(suite); 714 | visit(obj[key]); 715 | suites.shift(); 716 | } 717 | } 718 | } 719 | }; 720 | }); // module: interfaces/exports.js 721 | 722 | require.register("interfaces/index.js", function(module, exports, require){ 723 | 724 | exports.bdd = require('./bdd'); 725 | exports.tdd = require('./tdd'); 726 | exports.qunit = require('./qunit'); 727 | exports.exports = require('./exports'); 728 | 729 | }); // module: interfaces/index.js 730 | 731 | require.register("interfaces/qunit.js", function(module, exports, require){ 732 | 733 | /** 734 | * Module dependencies. 735 | */ 736 | 737 | var Suite = require('../suite') 738 | , Test = require('../test'); 739 | 740 | /** 741 | * QUnit-style interface: 742 | * 743 | * suite('Array'); 744 | * 745 | * test('#length', function(){ 746 | * var arr = [1,2,3]; 747 | * ok(arr.length == 3); 748 | * }); 749 | * 750 | * test('#indexOf()', function(){ 751 | * var arr = [1,2,3]; 752 | * ok(arr.indexOf(1) == 0); 753 | * ok(arr.indexOf(2) == 1); 754 | * ok(arr.indexOf(3) == 2); 755 | * }); 756 | * 757 | * suite('String'); 758 | * 759 | * test('#length', function(){ 760 | * ok('foo'.length == 3); 761 | * }); 762 | * 763 | */ 764 | 765 | module.exports = function(suite){ 766 | var suites = [suite]; 767 | 768 | suite.on('pre-require', function(context){ 769 | 770 | /** 771 | * Execute before running tests. 772 | */ 773 | 774 | context.before = function(fn){ 775 | suites[0].beforeAll(fn); 776 | }; 777 | 778 | /** 779 | * Execute after running tests. 780 | */ 781 | 782 | context.after = function(fn){ 783 | suites[0].afterAll(fn); 784 | }; 785 | 786 | /** 787 | * Execute before each test case. 788 | */ 789 | 790 | context.beforeEach = function(fn){ 791 | suites[0].beforeEach(fn); 792 | }; 793 | 794 | /** 795 | * Execute after each test case. 796 | */ 797 | 798 | context.afterEach = function(fn){ 799 | suites[0].afterEach(fn); 800 | }; 801 | 802 | /** 803 | * Describe a "suite" with the given `title`. 804 | */ 805 | 806 | context.suite = function(title){ 807 | if (suites.length > 1) suites.shift(); 808 | var suite = Suite.create(suites[0], title); 809 | suites.unshift(suite); 810 | }; 811 | 812 | /** 813 | * Describe a specification or test-case 814 | * with the given `title` and callback `fn` 815 | * acting as a thunk. 816 | */ 817 | 818 | context.test = function(title, fn){ 819 | suites[0].addTest(new Test(title, fn)); 820 | }; 821 | }); 822 | }; 823 | 824 | }); // module: interfaces/qunit.js 825 | 826 | require.register("interfaces/tdd.js", function(module, exports, require){ 827 | 828 | /** 829 | * Module dependencies. 830 | */ 831 | 832 | var Suite = require('../suite') 833 | , Test = require('../test'); 834 | 835 | /** 836 | * TDD-style interface: 837 | * 838 | * suite('Array', function(){ 839 | * suite('#indexOf()', function(){ 840 | * suiteSetup(function(){ 841 | * 842 | * }); 843 | * 844 | * test('should return -1 when not present', function(){ 845 | * 846 | * }); 847 | * 848 | * test('should return the index when present', function(){ 849 | * 850 | * }); 851 | * 852 | * suiteTeardown(function(){ 853 | * 854 | * }); 855 | * }); 856 | * }); 857 | * 858 | */ 859 | 860 | module.exports = function(suite){ 861 | var suites = [suite]; 862 | 863 | suite.on('pre-require', function(context, file, mocha){ 864 | 865 | /** 866 | * Execute before each test case. 867 | */ 868 | 869 | context.setup = function(fn){ 870 | suites[0].beforeEach(fn); 871 | }; 872 | 873 | /** 874 | * Execute after each test case. 875 | */ 876 | 877 | context.teardown = function(fn){ 878 | suites[0].afterEach(fn); 879 | }; 880 | 881 | /** 882 | * Execute before the suite. 883 | */ 884 | 885 | context.suiteSetup = function(fn){ 886 | suites[0].beforeAll(fn); 887 | }; 888 | 889 | /** 890 | * Execute after the suite. 891 | */ 892 | 893 | context.suiteTeardown = function(fn){ 894 | suites[0].afterAll(fn); 895 | }; 896 | 897 | /** 898 | * Describe a "suite" with the given `title` 899 | * and callback `fn` containing nested suites 900 | * and/or tests. 901 | */ 902 | 903 | context.suite = function(title, fn){ 904 | var suite = Suite.create(suites[0], title); 905 | suites.unshift(suite); 906 | fn.call(suite); 907 | suites.shift(); 908 | return suite; 909 | }; 910 | 911 | /** 912 | * Exclusive test-case. 913 | */ 914 | 915 | context.suite.only = function(title, fn){ 916 | var suite = context.suite(title, fn); 917 | mocha.grep(suite.fullTitle()); 918 | }; 919 | 920 | /** 921 | * Describe a specification or test-case 922 | * with the given `title` and callback `fn` 923 | * acting as a thunk. 924 | */ 925 | 926 | context.test = function(title, fn){ 927 | var test = new Test(title, fn); 928 | suites[0].addTest(test); 929 | return test; 930 | }; 931 | 932 | /** 933 | * Exclusive test-case. 934 | */ 935 | 936 | context.test.only = function(title, fn){ 937 | var test = context.test(title, fn); 938 | mocha.grep(test.fullTitle()); 939 | }; 940 | 941 | /** 942 | * Pending test case. 943 | */ 944 | 945 | context.test.skip = function(title){ 946 | context.test(title); 947 | }; 948 | }); 949 | }; 950 | 951 | }); // module: interfaces/tdd.js 952 | 953 | require.register("mocha.js", function(module, exports, require){ 954 | /*! 955 | * mocha 956 | * Copyright(c) 2011 TJ Holowaychuk 957 | * MIT Licensed 958 | */ 959 | 960 | /** 961 | * Module dependencies. 962 | */ 963 | 964 | var path = require('browser/path') 965 | , utils = require('./utils'); 966 | 967 | /** 968 | * Expose `Mocha`. 969 | */ 970 | 971 | exports = module.exports = Mocha; 972 | 973 | /** 974 | * Expose internals. 975 | */ 976 | 977 | exports.utils = utils; 978 | exports.interfaces = require('./interfaces'); 979 | exports.reporters = require('./reporters'); 980 | exports.Runnable = require('./runnable'); 981 | exports.Context = require('./context'); 982 | exports.Runner = require('./runner'); 983 | exports.Suite = require('./suite'); 984 | exports.Hook = require('./hook'); 985 | exports.Test = require('./test'); 986 | 987 | /** 988 | * Return image `name` path. 989 | * 990 | * @param {String} name 991 | * @return {String} 992 | * @api private 993 | */ 994 | 995 | function image(name) { 996 | return __dirname + '/../images/' + name + '.png'; 997 | } 998 | 999 | /** 1000 | * Setup mocha with `options`. 1001 | * 1002 | * Options: 1003 | * 1004 | * - `ui` name "bdd", "tdd", "exports" etc 1005 | * - `reporter` reporter instance, defaults to `mocha.reporters.Dot` 1006 | * - `globals` array of accepted globals 1007 | * - `timeout` timeout in milliseconds 1008 | * - `slow` milliseconds to wait before considering a test slow 1009 | * - `ignoreLeaks` ignore global leaks 1010 | * - `grep` string or regexp to filter tests with 1011 | * 1012 | * @param {Object} options 1013 | * @api public 1014 | */ 1015 | 1016 | function Mocha(options) { 1017 | options = options || {}; 1018 | this.files = []; 1019 | this.options = options; 1020 | this.grep(options.grep); 1021 | this.suite = new exports.Suite('', new exports.Context); 1022 | this.ui(options.ui); 1023 | this.reporter(options.reporter); 1024 | if (options.timeout) this.timeout(options.timeout); 1025 | if (options.slow) this.slow(options.slow); 1026 | } 1027 | 1028 | /** 1029 | * Add test `file`. 1030 | * 1031 | * @param {String} file 1032 | * @api public 1033 | */ 1034 | 1035 | Mocha.prototype.addFile = function(file){ 1036 | this.files.push(file); 1037 | return this; 1038 | }; 1039 | 1040 | /** 1041 | * Set reporter to `reporter`, defaults to "dot". 1042 | * 1043 | * @param {String|Function} reporter name of a reporter or a reporter constructor 1044 | * @api public 1045 | */ 1046 | 1047 | Mocha.prototype.reporter = function(reporter){ 1048 | if ('function' == typeof reporter) { 1049 | this._reporter = reporter; 1050 | } else { 1051 | reporter = reporter || 'dot'; 1052 | try { 1053 | this._reporter = require('./reporters/' + reporter); 1054 | } catch (err) { 1055 | this._reporter = require(reporter); 1056 | } 1057 | if (!this._reporter) throw new Error('invalid reporter "' + reporter + '"'); 1058 | } 1059 | return this; 1060 | }; 1061 | 1062 | /** 1063 | * Set test UI `name`, defaults to "bdd". 1064 | * 1065 | * @param {String} bdd 1066 | * @api public 1067 | */ 1068 | 1069 | Mocha.prototype.ui = function(name){ 1070 | name = name || 'bdd'; 1071 | this._ui = exports.interfaces[name]; 1072 | if (!this._ui) throw new Error('invalid interface "' + name + '"'); 1073 | this._ui = this._ui(this.suite); 1074 | return this; 1075 | }; 1076 | 1077 | /** 1078 | * Load registered files. 1079 | * 1080 | * @api private 1081 | */ 1082 | 1083 | Mocha.prototype.loadFiles = function(fn){ 1084 | var self = this; 1085 | var suite = this.suite; 1086 | var pending = this.files.length; 1087 | this.files.forEach(function(file){ 1088 | file = path.resolve(file); 1089 | suite.emit('pre-require', global, file, self); 1090 | suite.emit('require', require(file), file, self); 1091 | suite.emit('post-require', global, file, self); 1092 | --pending || (fn && fn()); 1093 | }); 1094 | }; 1095 | 1096 | /** 1097 | * Enable growl support. 1098 | * 1099 | * @api private 1100 | */ 1101 | 1102 | Mocha.prototype._growl = function(runner, reporter) { 1103 | var notify = require('growl'); 1104 | 1105 | runner.on('end', function(){ 1106 | var stats = reporter.stats; 1107 | if (stats.failures) { 1108 | var msg = stats.failures + ' of ' + runner.total + ' tests failed'; 1109 | notify(msg, { name: 'mocha', title: 'Failed', image: image('error') }); 1110 | } else { 1111 | notify(stats.passes + ' tests passed in ' + stats.duration + 'ms', { 1112 | name: 'mocha' 1113 | , title: 'Passed' 1114 | , image: image('ok') 1115 | }); 1116 | } 1117 | }); 1118 | }; 1119 | 1120 | /** 1121 | * Add regexp to grep, if `re` is a string it is escaped. 1122 | * 1123 | * @param {RegExp|String} re 1124 | * @return {Mocha} 1125 | * @api public 1126 | */ 1127 | 1128 | Mocha.prototype.grep = function(re){ 1129 | this.options.grep = 'string' == typeof re 1130 | ? new RegExp(utils.escapeRegexp(re)) 1131 | : re; 1132 | return this; 1133 | }; 1134 | 1135 | /** 1136 | * Invert `.grep()` matches. 1137 | * 1138 | * @return {Mocha} 1139 | * @api public 1140 | */ 1141 | 1142 | Mocha.prototype.invert = function(){ 1143 | this.options.invert = true; 1144 | return this; 1145 | }; 1146 | 1147 | /** 1148 | * Ignore global leaks. 1149 | * 1150 | * @return {Mocha} 1151 | * @api public 1152 | */ 1153 | 1154 | Mocha.prototype.ignoreLeaks = function(){ 1155 | this.options.ignoreLeaks = true; 1156 | return this; 1157 | }; 1158 | 1159 | /** 1160 | * Enable global leak checking. 1161 | * 1162 | * @return {Mocha} 1163 | * @api public 1164 | */ 1165 | 1166 | Mocha.prototype.checkLeaks = function(){ 1167 | this.options.ignoreLeaks = false; 1168 | return this; 1169 | }; 1170 | 1171 | /** 1172 | * Enable growl support. 1173 | * 1174 | * @return {Mocha} 1175 | * @api public 1176 | */ 1177 | 1178 | Mocha.prototype.growl = function(){ 1179 | this.options.growl = true; 1180 | return this; 1181 | }; 1182 | 1183 | /** 1184 | * Ignore `globals` array or string. 1185 | * 1186 | * @param {Array|String} globals 1187 | * @return {Mocha} 1188 | * @api public 1189 | */ 1190 | 1191 | Mocha.prototype.globals = function(globals){ 1192 | this.options.globals = (this.options.globals || []).concat(globals); 1193 | return this; 1194 | }; 1195 | 1196 | /** 1197 | * Set the timeout in milliseconds. 1198 | * 1199 | * @param {Number} timeout 1200 | * @return {Mocha} 1201 | * @api public 1202 | */ 1203 | 1204 | Mocha.prototype.timeout = function(timeout){ 1205 | this.suite.timeout(timeout); 1206 | return this; 1207 | }; 1208 | 1209 | /** 1210 | * Set slowness threshold in milliseconds. 1211 | * 1212 | * @param {Number} slow 1213 | * @return {Mocha} 1214 | * @api public 1215 | */ 1216 | 1217 | Mocha.prototype.slow = function(slow){ 1218 | this.suite.slow(slow); 1219 | return this; 1220 | }; 1221 | 1222 | /** 1223 | * Makes all tests async (accepting a callback) 1224 | * 1225 | * @return {Mocha} 1226 | * @api public 1227 | */ 1228 | 1229 | Mocha.prototype.asyncOnly = function(){ 1230 | this.options.asyncOnly = true; 1231 | return this; 1232 | }; 1233 | 1234 | /** 1235 | * Run tests and invoke `fn()` when complete. 1236 | * 1237 | * @param {Function} fn 1238 | * @return {Runner} 1239 | * @api public 1240 | */ 1241 | 1242 | Mocha.prototype.run = function(fn){ 1243 | if (this.files.length) this.loadFiles(); 1244 | var suite = this.suite; 1245 | var options = this.options; 1246 | var runner = new exports.Runner(suite); 1247 | var reporter = new this._reporter(runner); 1248 | runner.ignoreLeaks = options.ignoreLeaks; 1249 | runner.asyncOnly = options.asyncOnly; 1250 | if (options.grep) runner.grep(options.grep, options.invert); 1251 | if (options.globals) runner.globals(options.globals); 1252 | if (options.growl) this._growl(runner, reporter); 1253 | return runner.run(fn); 1254 | }; 1255 | 1256 | }); // module: mocha.js 1257 | 1258 | require.register("ms.js", function(module, exports, require){ 1259 | 1260 | /** 1261 | * Helpers. 1262 | */ 1263 | 1264 | var s = 1000; 1265 | var m = s * 60; 1266 | var h = m * 60; 1267 | var d = h * 24; 1268 | 1269 | /** 1270 | * Parse or format the given `val`. 1271 | * 1272 | * @param {String|Number} val 1273 | * @return {String|Number} 1274 | * @api public 1275 | */ 1276 | 1277 | module.exports = function(val){ 1278 | if ('string' == typeof val) return parse(val); 1279 | return format(val); 1280 | } 1281 | 1282 | /** 1283 | * Parse the given `str` and return milliseconds. 1284 | * 1285 | * @param {String} str 1286 | * @return {Number} 1287 | * @api private 1288 | */ 1289 | 1290 | function parse(str) { 1291 | var m = /^((?:\d+)?\.?\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i.exec(str); 1292 | if (!m) return; 1293 | var n = parseFloat(m[1]); 1294 | var type = (m[2] || 'ms').toLowerCase(); 1295 | switch (type) { 1296 | case 'years': 1297 | case 'year': 1298 | case 'y': 1299 | return n * 31557600000; 1300 | case 'days': 1301 | case 'day': 1302 | case 'd': 1303 | return n * 86400000; 1304 | case 'hours': 1305 | case 'hour': 1306 | case 'h': 1307 | return n * 3600000; 1308 | case 'minutes': 1309 | case 'minute': 1310 | case 'm': 1311 | return n * 60000; 1312 | case 'seconds': 1313 | case 'second': 1314 | case 's': 1315 | return n * 1000; 1316 | case 'ms': 1317 | return n; 1318 | } 1319 | } 1320 | 1321 | /** 1322 | * Format the given `ms`. 1323 | * 1324 | * @param {Number} ms 1325 | * @return {String} 1326 | * @api public 1327 | */ 1328 | 1329 | function format(ms) { 1330 | if (ms == d) return Math.round(ms / d) + ' day'; 1331 | if (ms > d) return Math.round(ms / d) + ' days'; 1332 | if (ms == h) return Math.round(ms / h) + ' hour'; 1333 | if (ms > h) return Math.round(ms / h) + ' hours'; 1334 | if (ms == m) return Math.round(ms / m) + ' minute'; 1335 | if (ms > m) return Math.round(ms / m) + ' minutes'; 1336 | if (ms == s) return Math.round(ms / s) + ' second'; 1337 | if (ms > s) return Math.round(ms / s) + ' seconds'; 1338 | return ms + ' ms'; 1339 | } 1340 | }); // module: ms.js 1341 | 1342 | require.register("reporters/base.js", function(module, exports, require){ 1343 | 1344 | /** 1345 | * Module dependencies. 1346 | */ 1347 | 1348 | var tty = require('browser/tty') 1349 | , diff = require('browser/diff') 1350 | , ms = require('../ms'); 1351 | 1352 | /** 1353 | * Save timer references to avoid Sinon interfering (see GH-237). 1354 | */ 1355 | 1356 | var Date = global.Date 1357 | , setTimeout = global.setTimeout 1358 | , setInterval = global.setInterval 1359 | , clearTimeout = global.clearTimeout 1360 | , clearInterval = global.clearInterval; 1361 | 1362 | /** 1363 | * Check if both stdio streams are associated with a tty. 1364 | */ 1365 | 1366 | var isatty = tty.isatty(1) && tty.isatty(2); 1367 | 1368 | /** 1369 | * Expose `Base`. 1370 | */ 1371 | 1372 | exports = module.exports = Base; 1373 | 1374 | /** 1375 | * Enable coloring by default. 1376 | */ 1377 | 1378 | exports.useColors = isatty; 1379 | 1380 | /** 1381 | * Default color map. 1382 | */ 1383 | 1384 | exports.colors = { 1385 | 'pass': 90 1386 | , 'fail': 31 1387 | , 'bright pass': 92 1388 | , 'bright fail': 91 1389 | , 'bright yellow': 93 1390 | , 'pending': 36 1391 | , 'suite': 0 1392 | , 'error title': 0 1393 | , 'error message': 31 1394 | , 'error stack': 90 1395 | , 'checkmark': 32 1396 | , 'fast': 90 1397 | , 'medium': 33 1398 | , 'slow': 31 1399 | , 'green': 32 1400 | , 'light': 90 1401 | , 'diff gutter': 90 1402 | , 'diff added': 42 1403 | , 'diff removed': 41 1404 | }; 1405 | 1406 | /** 1407 | * Default symbol map. 1408 | */ 1409 | 1410 | exports.symbols = { 1411 | ok: '✓', 1412 | err: '✖', 1413 | dot: '․' 1414 | }; 1415 | 1416 | // With node.js on Windows: use symbols available in terminal default fonts 1417 | if ('win32' == process.platform) { 1418 | exports.symbols.ok = '\u221A'; 1419 | exports.symbols.err = '\u00D7'; 1420 | exports.symbols.dot = '.'; 1421 | } 1422 | 1423 | /** 1424 | * Color `str` with the given `type`, 1425 | * allowing colors to be disabled, 1426 | * as well as user-defined color 1427 | * schemes. 1428 | * 1429 | * @param {String} type 1430 | * @param {String} str 1431 | * @return {String} 1432 | * @api private 1433 | */ 1434 | 1435 | var color = exports.color = function(type, str) { 1436 | if (!exports.useColors) return str; 1437 | return '\u001b[' + exports.colors[type] + 'm' + str + '\u001b[0m'; 1438 | }; 1439 | 1440 | /** 1441 | * Expose term window size, with some 1442 | * defaults for when stderr is not a tty. 1443 | */ 1444 | 1445 | exports.window = { 1446 | width: isatty 1447 | ? process.stdout.getWindowSize 1448 | ? process.stdout.getWindowSize(1)[0] 1449 | : tty.getWindowSize()[1] 1450 | : 75 1451 | }; 1452 | 1453 | /** 1454 | * Expose some basic cursor interactions 1455 | * that are common among reporters. 1456 | */ 1457 | 1458 | exports.cursor = { 1459 | hide: function(){ 1460 | process.stdout.write('\u001b[?25l'); 1461 | }, 1462 | 1463 | show: function(){ 1464 | process.stdout.write('\u001b[?25h'); 1465 | }, 1466 | 1467 | deleteLine: function(){ 1468 | process.stdout.write('\u001b[2K'); 1469 | }, 1470 | 1471 | beginningOfLine: function(){ 1472 | process.stdout.write('\u001b[0G'); 1473 | }, 1474 | 1475 | CR: function(){ 1476 | exports.cursor.deleteLine(); 1477 | exports.cursor.beginningOfLine(); 1478 | } 1479 | }; 1480 | 1481 | /** 1482 | * Outut the given `failures` as a list. 1483 | * 1484 | * @param {Array} failures 1485 | * @api public 1486 | */ 1487 | 1488 | exports.list = function(failures){ 1489 | console.error(); 1490 | failures.forEach(function(test, i){ 1491 | // format 1492 | var fmt = color('error title', ' %s) %s:\n') 1493 | + color('error message', ' %s') 1494 | + color('error stack', '\n%s\n'); 1495 | 1496 | // msg 1497 | var err = test.err 1498 | , message = err.message || '' 1499 | , stack = err.stack || message 1500 | , index = stack.indexOf(message) + message.length 1501 | , msg = stack.slice(0, index) 1502 | , actual = err.actual 1503 | , expected = err.expected 1504 | , escape = true; 1505 | 1506 | // explicitly show diff 1507 | if (err.showDiff) { 1508 | escape = false; 1509 | err.actual = actual = JSON.stringify(actual, null, 2); 1510 | err.expected = expected = JSON.stringify(expected, null, 2); 1511 | } 1512 | 1513 | // actual / expected diff 1514 | if ('string' == typeof actual && 'string' == typeof expected) { 1515 | var len = Math.max(actual.length, expected.length); 1516 | 1517 | if (len < 20) msg = errorDiff(err, 'Chars', escape); 1518 | else msg = errorDiff(err, 'Words', escape); 1519 | 1520 | // linenos 1521 | var lines = msg.split('\n'); 1522 | if (lines.length > 4) { 1523 | var width = String(lines.length).length; 1524 | msg = lines.map(function(str, i){ 1525 | return pad(++i, width) + ' |' + ' ' + str; 1526 | }).join('\n'); 1527 | } 1528 | 1529 | // legend 1530 | msg = '\n' 1531 | + color('diff removed', 'actual') 1532 | + ' ' 1533 | + color('diff added', 'expected') 1534 | + '\n\n' 1535 | + msg 1536 | + '\n'; 1537 | 1538 | // indent 1539 | msg = msg.replace(/^/gm, ' '); 1540 | 1541 | fmt = color('error title', ' %s) %s:\n%s') 1542 | + color('error stack', '\n%s\n'); 1543 | } 1544 | 1545 | // indent stack trace without msg 1546 | stack = stack.slice(index ? index + 1 : index) 1547 | .replace(/^/gm, ' '); 1548 | 1549 | console.error(fmt, (i + 1), test.fullTitle(), msg, stack); 1550 | }); 1551 | }; 1552 | 1553 | /** 1554 | * Initialize a new `Base` reporter. 1555 | * 1556 | * All other reporters generally 1557 | * inherit from this reporter, providing 1558 | * stats such as test duration, number 1559 | * of tests passed / failed etc. 1560 | * 1561 | * @param {Runner} runner 1562 | * @api public 1563 | */ 1564 | 1565 | function Base(runner) { 1566 | var self = this 1567 | , stats = this.stats = { suites: 0, tests: 0, passes: 0, pending: 0, failures: 0 } 1568 | , failures = this.failures = []; 1569 | 1570 | if (!runner) return; 1571 | this.runner = runner; 1572 | 1573 | runner.stats = stats; 1574 | 1575 | runner.on('start', function(){ 1576 | stats.start = new Date; 1577 | }); 1578 | 1579 | runner.on('suite', function(suite){ 1580 | stats.suites = stats.suites || 0; 1581 | suite.root || stats.suites++; 1582 | }); 1583 | 1584 | runner.on('test end', function(test){ 1585 | stats.tests = stats.tests || 0; 1586 | stats.tests++; 1587 | }); 1588 | 1589 | runner.on('pass', function(test){ 1590 | stats.passes = stats.passes || 0; 1591 | 1592 | var medium = test.slow() / 2; 1593 | test.speed = test.duration > test.slow() 1594 | ? 'slow' 1595 | : test.duration > medium 1596 | ? 'medium' 1597 | : 'fast'; 1598 | 1599 | stats.passes++; 1600 | }); 1601 | 1602 | runner.on('fail', function(test, err){ 1603 | stats.failures = stats.failures || 0; 1604 | stats.failures++; 1605 | test.err = err; 1606 | failures.push(test); 1607 | }); 1608 | 1609 | runner.on('end', function(){ 1610 | stats.end = new Date; 1611 | stats.duration = new Date - stats.start; 1612 | }); 1613 | 1614 | runner.on('pending', function(){ 1615 | stats.pending++; 1616 | }); 1617 | } 1618 | 1619 | /** 1620 | * Output common epilogue used by many of 1621 | * the bundled reporters. 1622 | * 1623 | * @api public 1624 | */ 1625 | 1626 | Base.prototype.epilogue = function(){ 1627 | var stats = this.stats 1628 | , fmt 1629 | , tests; 1630 | 1631 | console.log(); 1632 | 1633 | function pluralize(n) { 1634 | return 1 == n ? 'test' : 'tests'; 1635 | } 1636 | 1637 | // failure 1638 | if (stats.failures) { 1639 | fmt = color('bright fail', ' ' + exports.symbols.err) 1640 | + color('fail', ' %d of %d %s failed') 1641 | + color('light', ':') 1642 | 1643 | console.error(fmt, 1644 | stats.failures, 1645 | this.runner.total, 1646 | pluralize(this.runner.total)); 1647 | 1648 | Base.list(this.failures); 1649 | console.error(); 1650 | return; 1651 | } 1652 | 1653 | // pass 1654 | fmt = color('bright pass', ' ') 1655 | + color('green', ' %d %s complete') 1656 | + color('light', ' (%s)'); 1657 | 1658 | console.log(fmt, 1659 | stats.tests || 0, 1660 | pluralize(stats.tests), 1661 | ms(stats.duration)); 1662 | 1663 | // pending 1664 | if (stats.pending) { 1665 | fmt = color('pending', ' ') 1666 | + color('pending', ' %d %s pending'); 1667 | 1668 | console.log(fmt, stats.pending, pluralize(stats.pending)); 1669 | } 1670 | 1671 | console.log(); 1672 | }; 1673 | 1674 | /** 1675 | * Pad the given `str` to `len`. 1676 | * 1677 | * @param {String} str 1678 | * @param {String} len 1679 | * @return {String} 1680 | * @api private 1681 | */ 1682 | 1683 | function pad(str, len) { 1684 | str = String(str); 1685 | return Array(len - str.length + 1).join(' ') + str; 1686 | } 1687 | 1688 | /** 1689 | * Return a character diff for `err`. 1690 | * 1691 | * @param {Error} err 1692 | * @return {String} 1693 | * @api private 1694 | */ 1695 | 1696 | function errorDiff(err, type, escape) { 1697 | return diff['diff' + type](err.actual, err.expected).map(function(str){ 1698 | if (escape) { 1699 | str.value = str.value 1700 | .replace(/\t/g, '') 1701 | .replace(/\r/g, '') 1702 | .replace(/\n/g, '\n'); 1703 | } 1704 | if (str.added) return colorLines('diff added', str.value); 1705 | if (str.removed) return colorLines('diff removed', str.value); 1706 | return str.value; 1707 | }).join(''); 1708 | } 1709 | 1710 | /** 1711 | * Color lines for `str`, using the color `name`. 1712 | * 1713 | * @param {String} name 1714 | * @param {String} str 1715 | * @return {String} 1716 | * @api private 1717 | */ 1718 | 1719 | function colorLines(name, str) { 1720 | return str.split('\n').map(function(str){ 1721 | return color(name, str); 1722 | }).join('\n'); 1723 | } 1724 | 1725 | }); // module: reporters/base.js 1726 | 1727 | require.register("reporters/doc.js", function(module, exports, require){ 1728 | 1729 | /** 1730 | * Module dependencies. 1731 | */ 1732 | 1733 | var Base = require('./base') 1734 | , utils = require('../utils'); 1735 | 1736 | /** 1737 | * Expose `Doc`. 1738 | */ 1739 | 1740 | exports = module.exports = Doc; 1741 | 1742 | /** 1743 | * Initialize a new `Doc` reporter. 1744 | * 1745 | * @param {Runner} runner 1746 | * @api public 1747 | */ 1748 | 1749 | function Doc(runner) { 1750 | Base.call(this, runner); 1751 | 1752 | var self = this 1753 | , stats = this.stats 1754 | , total = runner.total 1755 | , indents = 2; 1756 | 1757 | function indent() { 1758 | return Array(indents).join(' '); 1759 | } 1760 | 1761 | runner.on('suite', function(suite){ 1762 | if (suite.root) return; 1763 | ++indents; 1764 | console.log('%s', indent()); 1765 | ++indents; 1766 | console.log('%s%s', indent(), utils.escape(suite.title)); 1767 | console.log('%s', indent()); 1768 | }); 1769 | 1770 | runner.on('suite end', function(suite){ 1771 | if (suite.root) return; 1772 | console.log('%s', indent()); 1773 | --indents; 1774 | console.log('%s', indent()); 1775 | --indents; 1776 | }); 1777 | 1778 | runner.on('pass', function(test){ 1779 | console.log('%s %s', indent(), utils.escape(test.title)); 1780 | var code = utils.escape(utils.clean(test.fn.toString())); 1781 | console.log('%s %s', indent(), code); 1782 | }); 1783 | } 1784 | 1785 | }); // module: reporters/doc.js 1786 | 1787 | require.register("reporters/dot.js", function(module, exports, require){ 1788 | 1789 | /** 1790 | * Module dependencies. 1791 | */ 1792 | 1793 | var Base = require('./base') 1794 | , color = Base.color; 1795 | 1796 | /** 1797 | * Expose `Dot`. 1798 | */ 1799 | 1800 | exports = module.exports = Dot; 1801 | 1802 | /** 1803 | * Initialize a new `Dot` matrix test reporter. 1804 | * 1805 | * @param {Runner} runner 1806 | * @api public 1807 | */ 1808 | 1809 | function Dot(runner) { 1810 | Base.call(this, runner); 1811 | 1812 | var self = this 1813 | , stats = this.stats 1814 | , width = Base.window.width * .75 | 0 1815 | , n = 0; 1816 | 1817 | runner.on('start', function(){ 1818 | process.stdout.write('\n '); 1819 | }); 1820 | 1821 | runner.on('pending', function(test){ 1822 | process.stdout.write(color('pending', Base.symbols.dot)); 1823 | }); 1824 | 1825 | runner.on('pass', function(test){ 1826 | if (++n % width == 0) process.stdout.write('\n '); 1827 | if ('slow' == test.speed) { 1828 | process.stdout.write(color('bright yellow', Base.symbols.dot)); 1829 | } else { 1830 | process.stdout.write(color(test.speed, Base.symbols.dot)); 1831 | } 1832 | }); 1833 | 1834 | runner.on('fail', function(test, err){ 1835 | if (++n % width == 0) process.stdout.write('\n '); 1836 | process.stdout.write(color('fail', Base.symbols.dot)); 1837 | }); 1838 | 1839 | runner.on('end', function(){ 1840 | console.log(); 1841 | self.epilogue(); 1842 | }); 1843 | } 1844 | 1845 | /** 1846 | * Inherit from `Base.prototype`. 1847 | */ 1848 | 1849 | Dot.prototype = new Base; 1850 | Dot.prototype.constructor = Dot; 1851 | 1852 | }); // module: reporters/dot.js 1853 | 1854 | require.register("reporters/html-cov.js", function(module, exports, require){ 1855 | 1856 | /** 1857 | * Module dependencies. 1858 | */ 1859 | 1860 | var JSONCov = require('./json-cov') 1861 | , fs = require('browser/fs'); 1862 | 1863 | /** 1864 | * Expose `HTMLCov`. 1865 | */ 1866 | 1867 | exports = module.exports = HTMLCov; 1868 | 1869 | /** 1870 | * Initialize a new `JsCoverage` reporter. 1871 | * 1872 | * @param {Runner} runner 1873 | * @api public 1874 | */ 1875 | 1876 | function HTMLCov(runner) { 1877 | var jade = require('jade') 1878 | , file = __dirname + '/templates/coverage.jade' 1879 | , str = fs.readFileSync(file, 'utf8') 1880 | , fn = jade.compile(str, { filename: file }) 1881 | , self = this; 1882 | 1883 | JSONCov.call(this, runner, false); 1884 | 1885 | runner.on('end', function(){ 1886 | process.stdout.write(fn({ 1887 | cov: self.cov 1888 | , coverageClass: coverageClass 1889 | })); 1890 | }); 1891 | } 1892 | 1893 | /** 1894 | * Return coverage class for `n`. 1895 | * 1896 | * @return {String} 1897 | * @api private 1898 | */ 1899 | 1900 | function coverageClass(n) { 1901 | if (n >= 75) return 'high'; 1902 | if (n >= 50) return 'medium'; 1903 | if (n >= 25) return 'low'; 1904 | return 'terrible'; 1905 | } 1906 | }); // module: reporters/html-cov.js 1907 | 1908 | require.register("reporters/html.js", function(module, exports, require){ 1909 | 1910 | /** 1911 | * Module dependencies. 1912 | */ 1913 | 1914 | var Base = require('./base') 1915 | , utils = require('../utils') 1916 | , Progress = require('../browser/progress') 1917 | , escape = utils.escape; 1918 | 1919 | /** 1920 | * Save timer references to avoid Sinon interfering (see GH-237). 1921 | */ 1922 | 1923 | var Date = global.Date 1924 | , setTimeout = global.setTimeout 1925 | , setInterval = global.setInterval 1926 | , clearTimeout = global.clearTimeout 1927 | , clearInterval = global.clearInterval; 1928 | 1929 | /** 1930 | * Expose `Doc`. 1931 | */ 1932 | 1933 | exports = module.exports = HTML; 1934 | 1935 | /** 1936 | * Stats template. 1937 | */ 1938 | 1939 | var statsTemplate = '' 1940 | + '' 1941 | + 'passes: 0' 1942 | + 'failures: 0' 1943 | + 'duration: 0s' 1944 | + ''; 1945 | 1946 | /** 1947 | * Initialize a new `Doc` reporter. 1948 | * 1949 | * @param {Runner} runner 1950 | * @api public 1951 | */ 1952 | 1953 | function HTML(runner, root) { 1954 | Base.call(this, runner); 1955 | 1956 | var self = this 1957 | , stats = this.stats 1958 | , total = runner.total 1959 | , stat = fragment(statsTemplate) 1960 | , items = stat.getElementsByTagName('li') 1961 | , passes = items[1].getElementsByTagName('em')[0] 1962 | , passesLink = items[1].getElementsByTagName('a')[0] 1963 | , failures = items[2].getElementsByTagName('em')[0] 1964 | , failuresLink = items[2].getElementsByTagName('a')[0] 1965 | , duration = items[3].getElementsByTagName('em')[0] 1966 | , canvas = stat.getElementsByTagName('canvas')[0] 1967 | , report = fragment('') 1968 | , stack = [report] 1969 | , progress 1970 | , ctx 1971 | 1972 | root = root || document.getElementById('mocha'); 1973 | 1974 | if (canvas.getContext) { 1975 | var ratio = window.devicePixelRatio || 1; 1976 | canvas.style.width = canvas.width; 1977 | canvas.style.height = canvas.height; 1978 | canvas.width *= ratio; 1979 | canvas.height *= ratio; 1980 | ctx = canvas.getContext('2d'); 1981 | ctx.scale(ratio, ratio); 1982 | progress = new Progress; 1983 | } 1984 | 1985 | if (!root) return error('#mocha div missing, add it to your document'); 1986 | 1987 | // pass toggle 1988 | on(passesLink, 'click', function(){ 1989 | unhide(); 1990 | var name = /pass/.test(report.className) ? '' : ' pass'; 1991 | report.className = report.className.replace(/fail|pass/g, '') + name; 1992 | if (report.className.trim()) hideSuitesWithout('test pass'); 1993 | }); 1994 | 1995 | // failure toggle 1996 | on(failuresLink, 'click', function(){ 1997 | unhide(); 1998 | var name = /fail/.test(report.className) ? '' : ' fail'; 1999 | report.className = report.className.replace(/fail|pass/g, '') + name; 2000 | if (report.className.trim()) hideSuitesWithout('test fail'); 2001 | }); 2002 | 2003 | root.appendChild(stat); 2004 | root.appendChild(report); 2005 | 2006 | if (progress) progress.size(40); 2007 | 2008 | runner.on('suite', function(suite){ 2009 | if (suite.root) return; 2010 | 2011 | // suite 2012 | var url = '?grep=' + encodeURIComponent(suite.fullTitle()); 2013 | var el = fragment('%s', url, escape(suite.title)); 2014 | 2015 | // container 2016 | stack[0].appendChild(el); 2017 | stack.unshift(document.createElement('ul')); 2018 | el.appendChild(stack[0]); 2019 | }); 2020 | 2021 | runner.on('suite end', function(suite){ 2022 | if (suite.root) return; 2023 | stack.shift(); 2024 | }); 2025 | 2026 | runner.on('fail', function(test, err){ 2027 | if ('hook' == test.type) runner.emit('test end', test); 2028 | }); 2029 | 2030 | runner.on('test end', function(test){ 2031 | window.scrollTo(0, document.body.scrollHeight); 2032 | 2033 | // TODO: add to stats 2034 | var percent = stats.tests / this.total * 100 | 0; 2035 | if (progress) progress.update(percent).draw(ctx); 2036 | 2037 | // update stats 2038 | var ms = new Date - stats.start; 2039 | text(passes, stats.passes); 2040 | text(failures, stats.failures); 2041 | text(duration, (ms / 1000).toFixed(2)); 2042 | 2043 | // test 2044 | if ('passed' == test.state) { 2045 | var el = fragment('%e%ems ‣', test.speed, test.title, test.duration, encodeURIComponent(test.fullTitle())); 2046 | } else if (test.pending) { 2047 | var el = fragment('%e', test.title); 2048 | } else { 2049 | var el = fragment('%e ‣', test.title, encodeURIComponent(test.fullTitle())); 2050 | var str = test.err.stack || test.err.toString(); 2051 | 2052 | // FF / Opera do not add the message 2053 | if (!~str.indexOf(test.err.message)) { 2054 | str = test.err.message + '\n' + str; 2055 | } 2056 | 2057 | // <=IE7 stringifies to [Object Error]. Since it can be overloaded, we 2058 | // check for the result of the stringifying. 2059 | if ('[object Error]' == str) str = test.err.message; 2060 | 2061 | // Safari doesn't give you a stack. Let's at least provide a source line. 2062 | if (!test.err.stack && test.err.sourceURL && test.err.line !== undefined) { 2063 | str += "\n(" + test.err.sourceURL + ":" + test.err.line + ")"; 2064 | } 2065 | 2066 | el.appendChild(fragment('%e', str)); 2067 | } 2068 | 2069 | // toggle code 2070 | // TODO: defer 2071 | if (!test.pending) { 2072 | var h2 = el.getElementsByTagName('h2')[0]; 2073 | 2074 | on(h2, 'click', function(){ 2075 | pre.style.display = 'none' == pre.style.display 2076 | ? 'inline-block' 2077 | : 'none'; 2078 | }); 2079 | 2080 | var pre = fragment('%e', utils.clean(test.fn.toString())); 2081 | el.appendChild(pre); 2082 | pre.style.display = 'none'; 2083 | } 2084 | 2085 | // Don't call .appendChild if #mocha-report was already .shift()'ed off the stack. 2086 | if (stack[0]) stack[0].appendChild(el); 2087 | }); 2088 | } 2089 | 2090 | /** 2091 | * Display error `msg`. 2092 | */ 2093 | 2094 | function error(msg) { 2095 | document.body.appendChild(fragment('%s', msg)); 2096 | } 2097 | 2098 | /** 2099 | * Return a DOM fragment from `html`. 2100 | */ 2101 | 2102 | function fragment(html) { 2103 | var args = arguments 2104 | , div = document.createElement('div') 2105 | , i = 1; 2106 | 2107 | div.innerHTML = html.replace(/%([se])/g, function(_, type){ 2108 | switch (type) { 2109 | case 's': return String(args[i++]); 2110 | case 'e': return escape(args[i++]); 2111 | } 2112 | }); 2113 | 2114 | return div.firstChild; 2115 | } 2116 | 2117 | /** 2118 | * Check for suites that do not have elements 2119 | * with `classname`, and hide them. 2120 | */ 2121 | 2122 | function hideSuitesWithout(classname) { 2123 | var suites = document.getElementsByClassName('suite'); 2124 | for (var i = 0; i < suites.length; i++) { 2125 | var els = suites[i].getElementsByClassName(classname); 2126 | if (0 == els.length) suites[i].className += ' hidden'; 2127 | } 2128 | } 2129 | 2130 | /** 2131 | * Unhide .hidden suites. 2132 | */ 2133 | 2134 | function unhide() { 2135 | var els = document.getElementsByClassName('suite hidden'); 2136 | for (var i = 0; i < els.length; ++i) { 2137 | els[i].className = els[i].className.replace('suite hidden', 'suite'); 2138 | } 2139 | } 2140 | 2141 | /** 2142 | * Set `el` text to `str`. 2143 | */ 2144 | 2145 | function text(el, str) { 2146 | if (el.textContent) { 2147 | el.textContent = str; 2148 | } else { 2149 | el.innerText = str; 2150 | } 2151 | } 2152 | 2153 | /** 2154 | * Listen on `event` with callback `fn`. 2155 | */ 2156 | 2157 | function on(el, event, fn) { 2158 | if (el.addEventListener) { 2159 | el.addEventListener(event, fn, false); 2160 | } else { 2161 | el.attachEvent('on' + event, fn); 2162 | } 2163 | } 2164 | 2165 | }); // module: reporters/html.js 2166 | 2167 | require.register("reporters/index.js", function(module, exports, require){ 2168 | 2169 | exports.Base = require('./base'); 2170 | exports.Dot = require('./dot'); 2171 | exports.Doc = require('./doc'); 2172 | exports.TAP = require('./tap'); 2173 | exports.JSON = require('./json'); 2174 | exports.HTML = require('./html'); 2175 | exports.List = require('./list'); 2176 | exports.Min = require('./min'); 2177 | exports.Spec = require('./spec'); 2178 | exports.Nyan = require('./nyan'); 2179 | exports.XUnit = require('./xunit'); 2180 | exports.Markdown = require('./markdown'); 2181 | exports.Progress = require('./progress'); 2182 | exports.Landing = require('./landing'); 2183 | exports.JSONCov = require('./json-cov'); 2184 | exports.HTMLCov = require('./html-cov'); 2185 | exports.JSONStream = require('./json-stream'); 2186 | exports.Teamcity = require('./teamcity'); 2187 | 2188 | }); // module: reporters/index.js 2189 | 2190 | require.register("reporters/json-cov.js", function(module, exports, require){ 2191 | 2192 | /** 2193 | * Module dependencies. 2194 | */ 2195 | 2196 | var Base = require('./base'); 2197 | 2198 | /** 2199 | * Expose `JSONCov`. 2200 | */ 2201 | 2202 | exports = module.exports = JSONCov; 2203 | 2204 | /** 2205 | * Initialize a new `JsCoverage` reporter. 2206 | * 2207 | * @param {Runner} runner 2208 | * @param {Boolean} output 2209 | * @api public 2210 | */ 2211 | 2212 | function JSONCov(runner, output) { 2213 | var self = this 2214 | , output = 1 == arguments.length ? true : output; 2215 | 2216 | Base.call(this, runner); 2217 | 2218 | var tests = [] 2219 | , failures = [] 2220 | , passes = []; 2221 | 2222 | runner.on('test end', function(test){ 2223 | tests.push(test); 2224 | }); 2225 | 2226 | runner.on('pass', function(test){ 2227 | passes.push(test); 2228 | }); 2229 | 2230 | runner.on('fail', function(test){ 2231 | failures.push(test); 2232 | }); 2233 | 2234 | runner.on('end', function(){ 2235 | var cov = global._$jscoverage || {}; 2236 | var result = self.cov = map(cov); 2237 | result.stats = self.stats; 2238 | result.tests = tests.map(clean); 2239 | result.failures = failures.map(clean); 2240 | result.passes = passes.map(clean); 2241 | if (!output) return; 2242 | process.stdout.write(JSON.stringify(result, null, 2 )); 2243 | }); 2244 | } 2245 | 2246 | /** 2247 | * Map jscoverage data to a JSON structure 2248 | * suitable for reporting. 2249 | * 2250 | * @param {Object} cov 2251 | * @return {Object} 2252 | * @api private 2253 | */ 2254 | 2255 | function map(cov) { 2256 | var ret = { 2257 | instrumentation: 'node-jscoverage' 2258 | , sloc: 0 2259 | , hits: 0 2260 | , misses: 0 2261 | , coverage: 0 2262 | , files: [] 2263 | }; 2264 | 2265 | for (var filename in cov) { 2266 | var data = coverage(filename, cov[filename]); 2267 | ret.files.push(data); 2268 | ret.hits += data.hits; 2269 | ret.misses += data.misses; 2270 | ret.sloc += data.sloc; 2271 | } 2272 | 2273 | ret.files.sort(function(a, b) { 2274 | return a.filename.localeCompare(b.filename); 2275 | }); 2276 | 2277 | if (ret.sloc > 0) { 2278 | ret.coverage = (ret.hits / ret.sloc) * 100; 2279 | } 2280 | 2281 | return ret; 2282 | }; 2283 | 2284 | /** 2285 | * Map jscoverage data for a single source file 2286 | * to a JSON structure suitable for reporting. 2287 | * 2288 | * @param {String} filename name of the source file 2289 | * @param {Object} data jscoverage coverage data 2290 | * @return {Object} 2291 | * @api private 2292 | */ 2293 | 2294 | function coverage(filename, data) { 2295 | var ret = { 2296 | filename: filename, 2297 | coverage: 0, 2298 | hits: 0, 2299 | misses: 0, 2300 | sloc: 0, 2301 | source: {} 2302 | }; 2303 | 2304 | data.source.forEach(function(line, num){ 2305 | num++; 2306 | 2307 | if (data[num] === 0) { 2308 | ret.misses++; 2309 | ret.sloc++; 2310 | } else if (data[num] !== undefined) { 2311 | ret.hits++; 2312 | ret.sloc++; 2313 | } 2314 | 2315 | ret.source[num] = { 2316 | source: line 2317 | , coverage: data[num] === undefined 2318 | ? '' 2319 | : data[num] 2320 | }; 2321 | }); 2322 | 2323 | ret.coverage = ret.hits / ret.sloc * 100; 2324 | 2325 | return ret; 2326 | } 2327 | 2328 | /** 2329 | * Return a plain-object representation of `test` 2330 | * free of cyclic properties etc. 2331 | * 2332 | * @param {Object} test 2333 | * @return {Object} 2334 | * @api private 2335 | */ 2336 | 2337 | function clean(test) { 2338 | return { 2339 | title: test.title 2340 | , fullTitle: test.fullTitle() 2341 | , duration: test.duration 2342 | } 2343 | } 2344 | 2345 | }); // module: reporters/json-cov.js 2346 | 2347 | require.register("reporters/json-stream.js", function(module, exports, require){ 2348 | 2349 | /** 2350 | * Module dependencies. 2351 | */ 2352 | 2353 | var Base = require('./base') 2354 | , color = Base.color; 2355 | 2356 | /** 2357 | * Expose `List`. 2358 | */ 2359 | 2360 | exports = module.exports = List; 2361 | 2362 | /** 2363 | * Initialize a new `List` test reporter. 2364 | * 2365 | * @param {Runner} runner 2366 | * @api public 2367 | */ 2368 | 2369 | function List(runner) { 2370 | Base.call(this, runner); 2371 | 2372 | var self = this 2373 | , stats = this.stats 2374 | , total = runner.total; 2375 | 2376 | runner.on('start', function(){ 2377 | console.log(JSON.stringify(['start', { total: total }])); 2378 | }); 2379 | 2380 | runner.on('pass', function(test){ 2381 | console.log(JSON.stringify(['pass', clean(test)])); 2382 | }); 2383 | 2384 | runner.on('fail', function(test, err){ 2385 | console.log(JSON.stringify(['fail', clean(test)])); 2386 | }); 2387 | 2388 | runner.on('end', function(){ 2389 | process.stdout.write(JSON.stringify(['end', self.stats])); 2390 | }); 2391 | } 2392 | 2393 | /** 2394 | * Return a plain-object representation of `test` 2395 | * free of cyclic properties etc. 2396 | * 2397 | * @param {Object} test 2398 | * @return {Object} 2399 | * @api private 2400 | */ 2401 | 2402 | function clean(test) { 2403 | return { 2404 | title: test.title 2405 | , fullTitle: test.fullTitle() 2406 | , duration: test.duration 2407 | } 2408 | } 2409 | }); // module: reporters/json-stream.js 2410 | 2411 | require.register("reporters/json.js", function(module, exports, require){ 2412 | 2413 | /** 2414 | * Module dependencies. 2415 | */ 2416 | 2417 | var Base = require('./base') 2418 | , cursor = Base.cursor 2419 | , color = Base.color; 2420 | 2421 | /** 2422 | * Expose `JSON`. 2423 | */ 2424 | 2425 | exports = module.exports = JSONReporter; 2426 | 2427 | /** 2428 | * Initialize a new `JSON` reporter. 2429 | * 2430 | * @param {Runner} runner 2431 | * @api public 2432 | */ 2433 | 2434 | function JSONReporter(runner) { 2435 | var self = this; 2436 | Base.call(this, runner); 2437 | 2438 | var tests = [] 2439 | , failures = [] 2440 | , passes = []; 2441 | 2442 | runner.on('test end', function(test){ 2443 | tests.push(test); 2444 | }); 2445 | 2446 | runner.on('pass', function(test){ 2447 | passes.push(test); 2448 | }); 2449 | 2450 | runner.on('fail', function(test){ 2451 | failures.push(test); 2452 | }); 2453 | 2454 | runner.on('end', function(){ 2455 | var obj = { 2456 | stats: self.stats 2457 | , tests: tests.map(clean) 2458 | , failures: failures.map(clean) 2459 | , passes: passes.map(clean) 2460 | }; 2461 | 2462 | process.stdout.write(JSON.stringify(obj, null, 2)); 2463 | }); 2464 | } 2465 | 2466 | /** 2467 | * Return a plain-object representation of `test` 2468 | * free of cyclic properties etc. 2469 | * 2470 | * @param {Object} test 2471 | * @return {Object} 2472 | * @api private 2473 | */ 2474 | 2475 | function clean(test) { 2476 | return { 2477 | title: test.title 2478 | , fullTitle: test.fullTitle() 2479 | , duration: test.duration 2480 | } 2481 | } 2482 | }); // module: reporters/json.js 2483 | 2484 | require.register("reporters/landing.js", function(module, exports, require){ 2485 | 2486 | /** 2487 | * Module dependencies. 2488 | */ 2489 | 2490 | var Base = require('./base') 2491 | , cursor = Base.cursor 2492 | , color = Base.color; 2493 | 2494 | /** 2495 | * Expose `Landing`. 2496 | */ 2497 | 2498 | exports = module.exports = Landing; 2499 | 2500 | /** 2501 | * Airplane color. 2502 | */ 2503 | 2504 | Base.colors.plane = 0; 2505 | 2506 | /** 2507 | * Airplane crash color. 2508 | */ 2509 | 2510 | Base.colors['plane crash'] = 31; 2511 | 2512 | /** 2513 | * Runway color. 2514 | */ 2515 | 2516 | Base.colors.runway = 90; 2517 | 2518 | /** 2519 | * Initialize a new `Landing` reporter. 2520 | * 2521 | * @param {Runner} runner 2522 | * @api public 2523 | */ 2524 | 2525 | function Landing(runner) { 2526 | Base.call(this, runner); 2527 | 2528 | var self = this 2529 | , stats = this.stats 2530 | , width = Base.window.width * .75 | 0 2531 | , total = runner.total 2532 | , stream = process.stdout 2533 | , plane = color('plane', '✈') 2534 | , crashed = -1 2535 | , n = 0; 2536 | 2537 | function runway() { 2538 | var buf = Array(width).join('-'); 2539 | return ' ' + color('runway', buf); 2540 | } 2541 | 2542 | runner.on('start', function(){ 2543 | stream.write('\n '); 2544 | cursor.hide(); 2545 | }); 2546 | 2547 | runner.on('test end', function(test){ 2548 | // check if the plane crashed 2549 | var col = -1 == crashed 2550 | ? width * ++n / total | 0 2551 | : crashed; 2552 | 2553 | // show the crash 2554 | if ('failed' == test.state) { 2555 | plane = color('plane crash', '✈'); 2556 | crashed = col; 2557 | } 2558 | 2559 | // render landing strip 2560 | stream.write('\u001b[4F\n\n'); 2561 | stream.write(runway()); 2562 | stream.write('\n '); 2563 | stream.write(color('runway', Array(col).join('⋅'))); 2564 | stream.write(plane) 2565 | stream.write(color('runway', Array(width - col).join('⋅') + '\n')); 2566 | stream.write(runway()); 2567 | stream.write('\u001b[0m'); 2568 | }); 2569 | 2570 | runner.on('end', function(){ 2571 | cursor.show(); 2572 | console.log(); 2573 | self.epilogue(); 2574 | }); 2575 | } 2576 | 2577 | /** 2578 | * Inherit from `Base.prototype`. 2579 | */ 2580 | 2581 | Landing.prototype = new Base; 2582 | Landing.prototype.constructor = Landing; 2583 | 2584 | }); // module: reporters/landing.js 2585 | 2586 | require.register("reporters/list.js", function(module, exports, require){ 2587 | 2588 | /** 2589 | * Module dependencies. 2590 | */ 2591 | 2592 | var Base = require('./base') 2593 | , cursor = Base.cursor 2594 | , color = Base.color; 2595 | 2596 | /** 2597 | * Expose `List`. 2598 | */ 2599 | 2600 | exports = module.exports = List; 2601 | 2602 | /** 2603 | * Initialize a new `List` test reporter. 2604 | * 2605 | * @param {Runner} runner 2606 | * @api public 2607 | */ 2608 | 2609 | function List(runner) { 2610 | Base.call(this, runner); 2611 | 2612 | var self = this 2613 | , stats = this.stats 2614 | , n = 0; 2615 | 2616 | runner.on('start', function(){ 2617 | console.log(); 2618 | }); 2619 | 2620 | runner.on('test', function(test){ 2621 | process.stdout.write(color('pass', ' ' + test.fullTitle() + ': ')); 2622 | }); 2623 | 2624 | runner.on('pending', function(test){ 2625 | var fmt = color('checkmark', ' -') 2626 | + color('pending', ' %s'); 2627 | console.log(fmt, test.fullTitle()); 2628 | }); 2629 | 2630 | runner.on('pass', function(test){ 2631 | var fmt = color('checkmark', ' '+Base.symbols.dot) 2632 | + color('pass', ' %s: ') 2633 | + color(test.speed, '%dms'); 2634 | cursor.CR(); 2635 | console.log(fmt, test.fullTitle(), test.duration); 2636 | }); 2637 | 2638 | runner.on('fail', function(test, err){ 2639 | cursor.CR(); 2640 | console.log(color('fail', ' %d) %s'), ++n, test.fullTitle()); 2641 | }); 2642 | 2643 | runner.on('end', self.epilogue.bind(self)); 2644 | } 2645 | 2646 | /** 2647 | * Inherit from `Base.prototype`. 2648 | */ 2649 | 2650 | List.prototype = new Base; 2651 | List.prototype.constructor = List; 2652 | 2653 | 2654 | }); // module: reporters/list.js 2655 | 2656 | require.register("reporters/markdown.js", function(module, exports, require){ 2657 | /** 2658 | * Module dependencies. 2659 | */ 2660 | 2661 | var Base = require('./base') 2662 | , utils = require('../utils'); 2663 | 2664 | /** 2665 | * Expose `Markdown`. 2666 | */ 2667 | 2668 | exports = module.exports = Markdown; 2669 | 2670 | /** 2671 | * Initialize a new `Markdown` reporter. 2672 | * 2673 | * @param {Runner} runner 2674 | * @api public 2675 | */ 2676 | 2677 | function Markdown(runner) { 2678 | Base.call(this, runner); 2679 | 2680 | var self = this 2681 | , stats = this.stats 2682 | , total = runner.total 2683 | , level = 0 2684 | , buf = ''; 2685 | 2686 | function title(str) { 2687 | return Array(level).join('#') + ' ' + str; 2688 | } 2689 | 2690 | function indent() { 2691 | return Array(level).join(' '); 2692 | } 2693 | 2694 | function mapTOC(suite, obj) { 2695 | var ret = obj; 2696 | obj = obj[suite.title] = obj[suite.title] || { suite: suite }; 2697 | suite.suites.forEach(function(suite){ 2698 | mapTOC(suite, obj); 2699 | }); 2700 | return ret; 2701 | } 2702 | 2703 | function stringifyTOC(obj, level) { 2704 | ++level; 2705 | var buf = ''; 2706 | var link; 2707 | for (var key in obj) { 2708 | if ('suite' == key) continue; 2709 | if (key) link = ' - [' + key + '](#' + utils.slug(obj[key].suite.fullTitle()) + ')\n'; 2710 | if (key) buf += Array(level).join(' ') + link; 2711 | buf += stringifyTOC(obj[key], level); 2712 | } 2713 | --level; 2714 | return buf; 2715 | } 2716 | 2717 | function generateTOC(suite) { 2718 | var obj = mapTOC(suite, {}); 2719 | return stringifyTOC(obj, 0); 2720 | } 2721 | 2722 | generateTOC(runner.suite); 2723 | 2724 | runner.on('suite', function(suite){ 2725 | ++level; 2726 | var slug = utils.slug(suite.fullTitle()); 2727 | buf += '' + '\n'; 2728 | buf += title(suite.title) + '\n'; 2729 | }); 2730 | 2731 | runner.on('suite end', function(suite){ 2732 | --level; 2733 | }); 2734 | 2735 | runner.on('pass', function(test){ 2736 | var code = utils.clean(test.fn.toString()); 2737 | buf += test.title + '.\n'; 2738 | buf += '\n```js\n'; 2739 | buf += code + '\n'; 2740 | buf += '```\n\n'; 2741 | }); 2742 | 2743 | runner.on('end', function(){ 2744 | process.stdout.write('# TOC\n'); 2745 | process.stdout.write(generateTOC(runner.suite)); 2746 | process.stdout.write(buf); 2747 | }); 2748 | } 2749 | }); // module: reporters/markdown.js 2750 | 2751 | require.register("reporters/min.js", function(module, exports, require){ 2752 | 2753 | /** 2754 | * Module dependencies. 2755 | */ 2756 | 2757 | var Base = require('./base'); 2758 | 2759 | /** 2760 | * Expose `Min`. 2761 | */ 2762 | 2763 | exports = module.exports = Min; 2764 | 2765 | /** 2766 | * Initialize a new `Min` minimal test reporter (best used with --watch). 2767 | * 2768 | * @param {Runner} runner 2769 | * @api public 2770 | */ 2771 | 2772 | function Min(runner) { 2773 | Base.call(this, runner); 2774 | 2775 | runner.on('start', function(){ 2776 | // clear screen 2777 | process.stdout.write('\u001b[2J'); 2778 | // set cursor position 2779 | process.stdout.write('\u001b[1;3H'); 2780 | }); 2781 | 2782 | runner.on('end', this.epilogue.bind(this)); 2783 | } 2784 | 2785 | /** 2786 | * Inherit from `Base.prototype`. 2787 | */ 2788 | 2789 | Min.prototype = new Base; 2790 | Min.prototype.constructor = Min; 2791 | 2792 | }); // module: reporters/min.js 2793 | 2794 | require.register("reporters/nyan.js", function(module, exports, require){ 2795 | 2796 | /** 2797 | * Module dependencies. 2798 | */ 2799 | 2800 | var Base = require('./base') 2801 | , color = Base.color; 2802 | 2803 | /** 2804 | * Expose `Dot`. 2805 | */ 2806 | 2807 | exports = module.exports = NyanCat; 2808 | 2809 | /** 2810 | * Initialize a new `Dot` matrix test reporter. 2811 | * 2812 | * @param {Runner} runner 2813 | * @api public 2814 | */ 2815 | 2816 | function NyanCat(runner) { 2817 | Base.call(this, runner); 2818 | 2819 | var self = this 2820 | , stats = this.stats 2821 | , width = Base.window.width * .75 | 0 2822 | , rainbowColors = this.rainbowColors = self.generateColors() 2823 | , colorIndex = this.colorIndex = 0 2824 | , numerOfLines = this.numberOfLines = 4 2825 | , trajectories = this.trajectories = [[], [], [], []] 2826 | , nyanCatWidth = this.nyanCatWidth = 11 2827 | , trajectoryWidthMax = this.trajectoryWidthMax = (width - nyanCatWidth) 2828 | , scoreboardWidth = this.scoreboardWidth = 5 2829 | , tick = this.tick = 0 2830 | , n = 0; 2831 | 2832 | runner.on('start', function(){ 2833 | Base.cursor.hide(); 2834 | self.draw('start'); 2835 | }); 2836 | 2837 | runner.on('pending', function(test){ 2838 | self.draw('pending'); 2839 | }); 2840 | 2841 | runner.on('pass', function(test){ 2842 | self.draw('pass'); 2843 | }); 2844 | 2845 | runner.on('fail', function(test, err){ 2846 | self.draw('fail'); 2847 | }); 2848 | 2849 | runner.on('end', function(){ 2850 | Base.cursor.show(); 2851 | for (var i = 0; i < self.numberOfLines; i++) write('\n'); 2852 | self.epilogue(); 2853 | }); 2854 | } 2855 | 2856 | /** 2857 | * Draw the nyan cat with runner `status`. 2858 | * 2859 | * @param {String} status 2860 | * @api private 2861 | */ 2862 | 2863 | NyanCat.prototype.draw = function(status){ 2864 | this.appendRainbow(); 2865 | this.drawScoreboard(); 2866 | this.drawRainbow(); 2867 | this.drawNyanCat(status); 2868 | this.tick = !this.tick; 2869 | }; 2870 | 2871 | /** 2872 | * Draw the "scoreboard" showing the number 2873 | * of passes, failures and pending tests. 2874 | * 2875 | * @api private 2876 | */ 2877 | 2878 | NyanCat.prototype.drawScoreboard = function(){ 2879 | var stats = this.stats; 2880 | var colors = Base.colors; 2881 | 2882 | function draw(color, n) { 2883 | write(' '); 2884 | write('\u001b[' + color + 'm' + n + '\u001b[0m'); 2885 | write('\n'); 2886 | } 2887 | 2888 | draw(colors.green, stats.passes); 2889 | draw(colors.fail, stats.failures); 2890 | draw(colors.pending, stats.pending); 2891 | write('\n'); 2892 | 2893 | this.cursorUp(this.numberOfLines); 2894 | }; 2895 | 2896 | /** 2897 | * Append the rainbow. 2898 | * 2899 | * @api private 2900 | */ 2901 | 2902 | NyanCat.prototype.appendRainbow = function(){ 2903 | var segment = this.tick ? '_' : '-'; 2904 | var rainbowified = this.rainbowify(segment); 2905 | 2906 | for (var index = 0; index < this.numberOfLines; index++) { 2907 | var trajectory = this.trajectories[index]; 2908 | if (trajectory.length >= this.trajectoryWidthMax) trajectory.shift(); 2909 | trajectory.push(rainbowified); 2910 | } 2911 | }; 2912 | 2913 | /** 2914 | * Draw the rainbow. 2915 | * 2916 | * @api private 2917 | */ 2918 | 2919 | NyanCat.prototype.drawRainbow = function(){ 2920 | var self = this; 2921 | 2922 | this.trajectories.forEach(function(line, index) { 2923 | write('\u001b[' + self.scoreboardWidth + 'C'); 2924 | write(line.join('')); 2925 | write('\n'); 2926 | }); 2927 | 2928 | this.cursorUp(this.numberOfLines); 2929 | }; 2930 | 2931 | /** 2932 | * Draw the nyan cat with `status`. 2933 | * 2934 | * @param {String} status 2935 | * @api private 2936 | */ 2937 | 2938 | NyanCat.prototype.drawNyanCat = function(status) { 2939 | var self = this; 2940 | var startWidth = this.scoreboardWidth + this.trajectories[0].length; 2941 | 2942 | [0, 1, 2, 3].forEach(function(index) { 2943 | write('\u001b[' + startWidth + 'C'); 2944 | 2945 | switch (index) { 2946 | case 0: 2947 | write('_,------,'); 2948 | write('\n'); 2949 | break; 2950 | case 1: 2951 | var padding = self.tick ? ' ' : ' '; 2952 | write('_|' + padding + '/\\_/\\ '); 2953 | write('\n'); 2954 | break; 2955 | case 2: 2956 | var padding = self.tick ? '_' : '__'; 2957 | var tail = self.tick ? '~' : '^'; 2958 | var face; 2959 | switch (status) { 2960 | case 'pass': 2961 | face = '( ^ .^)'; 2962 | break; 2963 | case 'fail': 2964 | face = '( o .o)'; 2965 | break; 2966 | default: 2967 | face = '( - .-)'; 2968 | } 2969 | write(tail + '|' + padding + face + ' '); 2970 | write('\n'); 2971 | break; 2972 | case 3: 2973 | var padding = self.tick ? ' ' : ' '; 2974 | write(padding + '"" "" '); 2975 | write('\n'); 2976 | break; 2977 | } 2978 | }); 2979 | 2980 | this.cursorUp(this.numberOfLines); 2981 | }; 2982 | 2983 | /** 2984 | * Move cursor up `n`. 2985 | * 2986 | * @param {Number} n 2987 | * @api private 2988 | */ 2989 | 2990 | NyanCat.prototype.cursorUp = function(n) { 2991 | write('\u001b[' + n + 'A'); 2992 | }; 2993 | 2994 | /** 2995 | * Move cursor down `n`. 2996 | * 2997 | * @param {Number} n 2998 | * @api private 2999 | */ 3000 | 3001 | NyanCat.prototype.cursorDown = function(n) { 3002 | write('\u001b[' + n + 'B'); 3003 | }; 3004 | 3005 | /** 3006 | * Generate rainbow colors. 3007 | * 3008 | * @return {Array} 3009 | * @api private 3010 | */ 3011 | 3012 | NyanCat.prototype.generateColors = function(){ 3013 | var colors = []; 3014 | 3015 | for (var i = 0; i < (6 * 7); i++) { 3016 | var pi3 = Math.floor(Math.PI / 3); 3017 | var n = (i * (1.0 / 6)); 3018 | var r = Math.floor(3 * Math.sin(n) + 3); 3019 | var g = Math.floor(3 * Math.sin(n + 2 * pi3) + 3); 3020 | var b = Math.floor(3 * Math.sin(n + 4 * pi3) + 3); 3021 | colors.push(36 * r + 6 * g + b + 16); 3022 | } 3023 | 3024 | return colors; 3025 | }; 3026 | 3027 | /** 3028 | * Apply rainbow to the given `str`. 3029 | * 3030 | * @param {String} str 3031 | * @return {String} 3032 | * @api private 3033 | */ 3034 | 3035 | NyanCat.prototype.rainbowify = function(str){ 3036 | var color = this.rainbowColors[this.colorIndex % this.rainbowColors.length]; 3037 | this.colorIndex += 1; 3038 | return '\u001b[38;5;' + color + 'm' + str + '\u001b[0m'; 3039 | }; 3040 | 3041 | /** 3042 | * Stdout helper. 3043 | */ 3044 | 3045 | function write(string) { 3046 | process.stdout.write(string); 3047 | } 3048 | 3049 | /** 3050 | * Inherit from `Base.prototype`. 3051 | */ 3052 | 3053 | NyanCat.prototype = new Base; 3054 | NyanCat.prototype.constructor = NyanCat; 3055 | 3056 | 3057 | }); // module: reporters/nyan.js 3058 | 3059 | require.register("reporters/progress.js", function(module, exports, require){ 3060 | 3061 | /** 3062 | * Module dependencies. 3063 | */ 3064 | 3065 | var Base = require('./base') 3066 | , cursor = Base.cursor 3067 | , color = Base.color; 3068 | 3069 | /** 3070 | * Expose `Progress`. 3071 | */ 3072 | 3073 | exports = module.exports = Progress; 3074 | 3075 | /** 3076 | * General progress bar color. 3077 | */ 3078 | 3079 | Base.colors.progress = 90; 3080 | 3081 | /** 3082 | * Initialize a new `Progress` bar test reporter. 3083 | * 3084 | * @param {Runner} runner 3085 | * @param {Object} options 3086 | * @api public 3087 | */ 3088 | 3089 | function Progress(runner, options) { 3090 | Base.call(this, runner); 3091 | 3092 | var self = this 3093 | , options = options || {} 3094 | , stats = this.stats 3095 | , width = Base.window.width * .50 | 0 3096 | , total = runner.total 3097 | , complete = 0 3098 | , max = Math.max; 3099 | 3100 | // default chars 3101 | options.open = options.open || '['; 3102 | options.complete = options.complete || '▬'; 3103 | options.incomplete = options.incomplete || Base.symbols.dot; 3104 | options.close = options.close || ']'; 3105 | options.verbose = false; 3106 | 3107 | // tests started 3108 | runner.on('start', function(){ 3109 | console.log(); 3110 | cursor.hide(); 3111 | }); 3112 | 3113 | // tests complete 3114 | runner.on('test end', function(){ 3115 | complete++; 3116 | var incomplete = total - complete 3117 | , percent = complete / total 3118 | , n = width * percent | 0 3119 | , i = width - n; 3120 | 3121 | cursor.CR(); 3122 | process.stdout.write('\u001b[J'); 3123 | process.stdout.write(color('progress', ' ' + options.open)); 3124 | process.stdout.write(Array(n).join(options.complete)); 3125 | process.stdout.write(Array(i).join(options.incomplete)); 3126 | process.stdout.write(color('progress', options.close)); 3127 | if (options.verbose) { 3128 | process.stdout.write(color('progress', ' ' + complete + ' of ' + total)); 3129 | } 3130 | }); 3131 | 3132 | // tests are complete, output some stats 3133 | // and the failures if any 3134 | runner.on('end', function(){ 3135 | cursor.show(); 3136 | console.log(); 3137 | self.epilogue(); 3138 | }); 3139 | } 3140 | 3141 | /** 3142 | * Inherit from `Base.prototype`. 3143 | */ 3144 | 3145 | Progress.prototype = new Base; 3146 | Progress.prototype.constructor = Progress; 3147 | 3148 | 3149 | }); // module: reporters/progress.js 3150 | 3151 | require.register("reporters/spec.js", function(module, exports, require){ 3152 | 3153 | /** 3154 | * Module dependencies. 3155 | */ 3156 | 3157 | var Base = require('./base') 3158 | , cursor = Base.cursor 3159 | , color = Base.color; 3160 | 3161 | /** 3162 | * Expose `Spec`. 3163 | */ 3164 | 3165 | exports = module.exports = Spec; 3166 | 3167 | /** 3168 | * Initialize a new `Spec` test reporter. 3169 | * 3170 | * @param {Runner} runner 3171 | * @api public 3172 | */ 3173 | 3174 | function Spec(runner) { 3175 | Base.call(this, runner); 3176 | 3177 | var self = this 3178 | , stats = this.stats 3179 | , indents = 0 3180 | , n = 0; 3181 | 3182 | function indent() { 3183 | return Array(indents).join(' ') 3184 | } 3185 | 3186 | runner.on('start', function(){ 3187 | console.log(); 3188 | }); 3189 | 3190 | runner.on('suite', function(suite){ 3191 | ++indents; 3192 | console.log(color('suite', '%s%s'), indent(), suite.title); 3193 | }); 3194 | 3195 | runner.on('suite end', function(suite){ 3196 | --indents; 3197 | if (1 == indents) console.log(); 3198 | }); 3199 | 3200 | runner.on('test', function(test){ 3201 | process.stdout.write(indent() + color('pass', ' ◦ ' + test.title + ': ')); 3202 | }); 3203 | 3204 | runner.on('pending', function(test){ 3205 | var fmt = indent() + color('pending', ' - %s'); 3206 | console.log(fmt, test.title); 3207 | }); 3208 | 3209 | runner.on('pass', function(test){ 3210 | if ('fast' == test.speed) { 3211 | var fmt = indent() 3212 | + color('checkmark', ' ' + Base.symbols.ok) 3213 | + color('pass', ' %s '); 3214 | cursor.CR(); 3215 | console.log(fmt, test.title); 3216 | } else { 3217 | var fmt = indent() 3218 | + color('checkmark', ' ' + Base.symbols.ok) 3219 | + color('pass', ' %s ') 3220 | + color(test.speed, '(%dms)'); 3221 | cursor.CR(); 3222 | console.log(fmt, test.title, test.duration); 3223 | } 3224 | }); 3225 | 3226 | runner.on('fail', function(test, err){ 3227 | cursor.CR(); 3228 | console.log(indent() + color('fail', ' %d) %s'), ++n, test.title); 3229 | }); 3230 | 3231 | runner.on('end', self.epilogue.bind(self)); 3232 | } 3233 | 3234 | /** 3235 | * Inherit from `Base.prototype`. 3236 | */ 3237 | 3238 | Spec.prototype = new Base; 3239 | Spec.prototype.constructor = Spec; 3240 | 3241 | 3242 | }); // module: reporters/spec.js 3243 | 3244 | require.register("reporters/tap.js", function(module, exports, require){ 3245 | 3246 | /** 3247 | * Module dependencies. 3248 | */ 3249 | 3250 | var Base = require('./base') 3251 | , cursor = Base.cursor 3252 | , color = Base.color; 3253 | 3254 | /** 3255 | * Expose `TAP`. 3256 | */ 3257 | 3258 | exports = module.exports = TAP; 3259 | 3260 | /** 3261 | * Initialize a new `TAP` reporter. 3262 | * 3263 | * @param {Runner} runner 3264 | * @api public 3265 | */ 3266 | 3267 | function TAP(runner) { 3268 | Base.call(this, runner); 3269 | 3270 | var self = this 3271 | , stats = this.stats 3272 | , n = 1 3273 | , passes = 0 3274 | , failures = 1; 3275 | 3276 | runner.on('start', function(){ 3277 | var total = runner.grepTotal(runner.suite); 3278 | console.log('%d..%d', 1, total); 3279 | }); 3280 | 3281 | runner.on('test end', function(){ 3282 | ++n; 3283 | }); 3284 | 3285 | runner.on('pending', function(test){ 3286 | console.log('ok %d %s # SKIP -', n, title(test)); 3287 | }); 3288 | 3289 | runner.on('pass', function(test){ 3290 | passes++; 3291 | console.log('ok %d %s', n, title(test)); 3292 | }); 3293 | 3294 | runner.on('fail', function(test, err){ 3295 | failures++; 3296 | console.log('not ok %d %s', n, title(test)); 3297 | console.log(err.stack.replace(/^/gm, ' ')); 3298 | }); 3299 | 3300 | runner.on('end', function(){ 3301 | console.log('# tests ' + (passes + failures)); 3302 | console.log('# pass ' + passes); 3303 | console.log('# fail ' + failures); 3304 | }); 3305 | } 3306 | 3307 | /** 3308 | * Return a TAP-safe title of `test` 3309 | * 3310 | * @param {Object} test 3311 | * @return {String} 3312 | * @api private 3313 | */ 3314 | 3315 | function title(test) { 3316 | return test.fullTitle().replace(/#/g, ''); 3317 | } 3318 | 3319 | }); // module: reporters/tap.js 3320 | 3321 | require.register("reporters/teamcity.js", function(module, exports, require){ 3322 | 3323 | /** 3324 | * Module dependencies. 3325 | */ 3326 | 3327 | var Base = require('./base'); 3328 | 3329 | /** 3330 | * Expose `Teamcity`. 3331 | */ 3332 | 3333 | exports = module.exports = Teamcity; 3334 | 3335 | /** 3336 | * Initialize a new `Teamcity` reporter. 3337 | * 3338 | * @param {Runner} runner 3339 | * @api public 3340 | */ 3341 | 3342 | function Teamcity(runner) { 3343 | Base.call(this, runner); 3344 | var stats = this.stats; 3345 | 3346 | runner.on('start', function() { 3347 | console.log("##teamcity[testSuiteStarted name='mocha.suite']"); 3348 | }); 3349 | 3350 | runner.on('test', function(test) { 3351 | console.log("##teamcity[testStarted name='" + escape(test.fullTitle()) + "']"); 3352 | }); 3353 | 3354 | runner.on('fail', function(test, err) { 3355 | console.log("##teamcity[testFailed name='" + escape(test.fullTitle()) + "' message='" + escape(err.message) + "']"); 3356 | }); 3357 | 3358 | runner.on('pending', function(test) { 3359 | console.log("##teamcity[testIgnored name='" + escape(test.fullTitle()) + "' message='pending']"); 3360 | }); 3361 | 3362 | runner.on('test end', function(test) { 3363 | console.log("##teamcity[testFinished name='" + escape(test.fullTitle()) + "' duration='" + test.duration + "']"); 3364 | }); 3365 | 3366 | runner.on('end', function() { 3367 | console.log("##teamcity[testSuiteFinished name='mocha.suite' duration='" + stats.duration + "']"); 3368 | }); 3369 | } 3370 | 3371 | /** 3372 | * Escape the given `str`. 3373 | */ 3374 | 3375 | function escape(str) { 3376 | return str 3377 | .replace(/\|/g, "||") 3378 | .replace(/\n/g, "|n") 3379 | .replace(/\r/g, "|r") 3380 | .replace(/\[/g, "|[") 3381 | .replace(/\]/g, "|]") 3382 | .replace(/\u0085/g, "|x") 3383 | .replace(/\u2028/g, "|l") 3384 | .replace(/\u2029/g, "|p") 3385 | .replace(/'/g, "|'"); 3386 | } 3387 | 3388 | }); // module: reporters/teamcity.js 3389 | 3390 | require.register("reporters/xunit.js", function(module, exports, require){ 3391 | 3392 | /** 3393 | * Module dependencies. 3394 | */ 3395 | 3396 | var Base = require('./base') 3397 | , utils = require('../utils') 3398 | , escape = utils.escape; 3399 | 3400 | /** 3401 | * Save timer references to avoid Sinon interfering (see GH-237). 3402 | */ 3403 | 3404 | var Date = global.Date 3405 | , setTimeout = global.setTimeout 3406 | , setInterval = global.setInterval 3407 | , clearTimeout = global.clearTimeout 3408 | , clearInterval = global.clearInterval; 3409 | 3410 | /** 3411 | * Expose `XUnit`. 3412 | */ 3413 | 3414 | exports = module.exports = XUnit; 3415 | 3416 | /** 3417 | * Initialize a new `XUnit` reporter. 3418 | * 3419 | * @param {Runner} runner 3420 | * @api public 3421 | */ 3422 | 3423 | function XUnit(runner) { 3424 | Base.call(this, runner); 3425 | var stats = this.stats 3426 | , tests = [] 3427 | , self = this; 3428 | 3429 | runner.on('pass', function(test){ 3430 | tests.push(test); 3431 | }); 3432 | 3433 | runner.on('fail', function(test){ 3434 | tests.push(test); 3435 | }); 3436 | 3437 | runner.on('end', function(){ 3438 | console.log(tag('testsuite', { 3439 | name: 'Mocha Tests' 3440 | , tests: stats.tests 3441 | , failures: stats.failures 3442 | , errors: stats.failures 3443 | , skip: stats.tests - stats.failures - stats.passes 3444 | , timestamp: (new Date).toUTCString() 3445 | , time: stats.duration / 1000 3446 | }, false)); 3447 | 3448 | tests.forEach(test); 3449 | console.log(''); 3450 | }); 3451 | } 3452 | 3453 | /** 3454 | * Inherit from `Base.prototype`. 3455 | */ 3456 | 3457 | XUnit.prototype = new Base; 3458 | XUnit.prototype.constructor = XUnit; 3459 | 3460 | 3461 | /** 3462 | * Output tag for the given `test.` 3463 | */ 3464 | 3465 | function test(test) { 3466 | var attrs = { 3467 | classname: test.parent.fullTitle() 3468 | , name: test.title 3469 | , time: test.duration / 1000 3470 | }; 3471 | 3472 | if ('failed' == test.state) { 3473 | var err = test.err; 3474 | attrs.message = escape(err.message); 3475 | console.log(tag('testcase', attrs, false, tag('failure', attrs, false, cdata(err.stack)))); 3476 | } else if (test.pending) { 3477 | console.log(tag('testcase', attrs, false, tag('skipped', {}, true))); 3478 | } else { 3479 | console.log(tag('testcase', attrs, true) ); 3480 | } 3481 | } 3482 | 3483 | /** 3484 | * HTML tag helper. 3485 | */ 3486 | 3487 | function tag(name, attrs, close, content) { 3488 | var end = close ? '/>' : '>' 3489 | , pairs = [] 3490 | , tag; 3491 | 3492 | for (var key in attrs) { 3493 | pairs.push(key + '="' + escape(attrs[key]) + '"'); 3494 | } 3495 | 3496 | tag = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end; 3497 | if (content) tag += content + '' + name + end; 3498 | return tag; 3499 | } 3500 | 3501 | /** 3502 | * Return cdata escaped CDATA `str`. 3503 | */ 3504 | 3505 | function cdata(str) { 3506 | return ''; 3507 | } 3508 | 3509 | }); // module: reporters/xunit.js 3510 | 3511 | require.register("runnable.js", function(module, exports, require){ 3512 | 3513 | /** 3514 | * Module dependencies. 3515 | */ 3516 | 3517 | var EventEmitter = require('browser/events').EventEmitter 3518 | , debug = require('browser/debug')('mocha:runnable') 3519 | , milliseconds = require('./ms'); 3520 | 3521 | /** 3522 | * Save timer references to avoid Sinon interfering (see GH-237). 3523 | */ 3524 | 3525 | var Date = global.Date 3526 | , setTimeout = global.setTimeout 3527 | , setInterval = global.setInterval 3528 | , clearTimeout = global.clearTimeout 3529 | , clearInterval = global.clearInterval; 3530 | 3531 | /** 3532 | * Object#toString(). 3533 | */ 3534 | 3535 | var toString = Object.prototype.toString; 3536 | 3537 | /** 3538 | * Expose `Runnable`. 3539 | */ 3540 | 3541 | module.exports = Runnable; 3542 | 3543 | /** 3544 | * Initialize a new `Runnable` with the given `title` and callback `fn`. 3545 | * 3546 | * @param {String} title 3547 | * @param {Function} fn 3548 | * @api private 3549 | */ 3550 | 3551 | function Runnable(title, fn) { 3552 | this.title = title; 3553 | this.fn = fn; 3554 | this.async = fn && fn.length; 3555 | this.sync = ! this.async; 3556 | this._timeout = 2000; 3557 | this._slow = 75; 3558 | this.timedOut = false; 3559 | } 3560 | 3561 | /** 3562 | * Inherit from `EventEmitter.prototype`. 3563 | */ 3564 | 3565 | Runnable.prototype = new EventEmitter; 3566 | Runnable.prototype.constructor = Runnable; 3567 | 3568 | 3569 | /** 3570 | * Set & get timeout `ms`. 3571 | * 3572 | * @param {Number|String} ms 3573 | * @return {Runnable|Number} ms or self 3574 | * @api private 3575 | */ 3576 | 3577 | Runnable.prototype.timeout = function(ms){ 3578 | if (0 == arguments.length) return this._timeout; 3579 | if ('string' == typeof ms) ms = milliseconds(ms); 3580 | debug('timeout %d', ms); 3581 | this._timeout = ms; 3582 | if (this.timer) this.resetTimeout(); 3583 | return this; 3584 | }; 3585 | 3586 | /** 3587 | * Set & get slow `ms`. 3588 | * 3589 | * @param {Number|String} ms 3590 | * @return {Runnable|Number} ms or self 3591 | * @api private 3592 | */ 3593 | 3594 | Runnable.prototype.slow = function(ms){ 3595 | if (0 === arguments.length) return this._slow; 3596 | if ('string' == typeof ms) ms = milliseconds(ms); 3597 | debug('timeout %d', ms); 3598 | this._slow = ms; 3599 | return this; 3600 | }; 3601 | 3602 | /** 3603 | * Return the full title generated by recursively 3604 | * concatenating the parent's full title. 3605 | * 3606 | * @return {String} 3607 | * @api public 3608 | */ 3609 | 3610 | Runnable.prototype.fullTitle = function(){ 3611 | return this.parent.fullTitle() + ' ' + this.title; 3612 | }; 3613 | 3614 | /** 3615 | * Clear the timeout. 3616 | * 3617 | * @api private 3618 | */ 3619 | 3620 | Runnable.prototype.clearTimeout = function(){ 3621 | clearTimeout(this.timer); 3622 | }; 3623 | 3624 | /** 3625 | * Inspect the runnable void of private properties. 3626 | * 3627 | * @return {String} 3628 | * @api private 3629 | */ 3630 | 3631 | Runnable.prototype.inspect = function(){ 3632 | return JSON.stringify(this, function(key, val){ 3633 | if ('_' == key[0]) return; 3634 | if ('parent' == key) return '#'; 3635 | if ('ctx' == key) return '#'; 3636 | return val; 3637 | }, 2); 3638 | }; 3639 | 3640 | /** 3641 | * Reset the timeout. 3642 | * 3643 | * @api private 3644 | */ 3645 | 3646 | Runnable.prototype.resetTimeout = function(){ 3647 | var self = this 3648 | , ms = this.timeout(); 3649 | 3650 | this.clearTimeout(); 3651 | if (ms) { 3652 | this.timer = setTimeout(function(){ 3653 | self.callback(new Error('timeout of ' + ms + 'ms exceeded')); 3654 | self.timedOut = true; 3655 | }, ms); 3656 | } 3657 | }; 3658 | 3659 | /** 3660 | * Run the test and invoke `fn(err)`. 3661 | * 3662 | * @param {Function} fn 3663 | * @api private 3664 | */ 3665 | 3666 | Runnable.prototype.run = function(fn){ 3667 | var self = this 3668 | , ms = this.timeout() 3669 | , start = new Date 3670 | , ctx = this.ctx 3671 | , finished 3672 | , emitted; 3673 | 3674 | if (ctx) ctx.runnable(this); 3675 | 3676 | // timeout 3677 | if (this.async) { 3678 | if (ms) { 3679 | this.timer = setTimeout(function(){ 3680 | done(new Error('timeout of ' + ms + 'ms exceeded')); 3681 | self.timedOut = true; 3682 | }, ms); 3683 | } 3684 | } 3685 | 3686 | // called multiple times 3687 | function multiple(err) { 3688 | if (emitted) return; 3689 | emitted = true; 3690 | self.emit('error', err || new Error('done() called multiple times')); 3691 | } 3692 | 3693 | // finished 3694 | function done(err) { 3695 | if (self.timedOut) return; 3696 | if (finished) return multiple(err); 3697 | self.clearTimeout(); 3698 | self.duration = new Date - start; 3699 | finished = true; 3700 | fn(err); 3701 | } 3702 | 3703 | // for .resetTimeout() 3704 | this.callback = done; 3705 | 3706 | // async 3707 | if (this.async) { 3708 | try { 3709 | this.fn.call(ctx, function(err){ 3710 | if (toString.call(err) === "[object Error]") return done(err); 3711 | if (null != err) return done(new Error('done() invoked with non-Error: ' + err)); 3712 | done(); 3713 | }); 3714 | } catch (err) { 3715 | done(err); 3716 | } 3717 | return; 3718 | } 3719 | 3720 | if (this.asyncOnly) { 3721 | return done(new Error('--async-only option in use without declaring `done()`')); 3722 | } 3723 | 3724 | // sync 3725 | try { 3726 | if (!this.pending) this.fn.call(ctx); 3727 | this.duration = new Date - start; 3728 | fn(); 3729 | } catch (err) { 3730 | fn(err); 3731 | } 3732 | }; 3733 | 3734 | }); // module: runnable.js 3735 | 3736 | require.register("runner.js", function(module, exports, require){ 3737 | 3738 | /** 3739 | * Module dependencies. 3740 | */ 3741 | 3742 | var EventEmitter = require('browser/events').EventEmitter 3743 | , debug = require('browser/debug')('mocha:runner') 3744 | , Test = require('./test') 3745 | , utils = require('./utils') 3746 | , filter = utils.filter 3747 | , keys = utils.keys 3748 | , noop = function(){}; 3749 | 3750 | /** 3751 | * Non-enumerable globals. 3752 | */ 3753 | 3754 | var globals = [ 3755 | 'setTimeout', 3756 | 'clearTimeout', 3757 | 'setInterval', 3758 | 'clearInterval', 3759 | 'XMLHttpRequest', 3760 | 'Date' 3761 | ]; 3762 | 3763 | /** 3764 | * Expose `Runner`. 3765 | */ 3766 | 3767 | module.exports = Runner; 3768 | 3769 | /** 3770 | * Initialize a `Runner` for the given `suite`. 3771 | * 3772 | * Events: 3773 | * 3774 | * - `start` execution started 3775 | * - `end` execution complete 3776 | * - `suite` (suite) test suite execution started 3777 | * - `suite end` (suite) all tests (and sub-suites) have finished 3778 | * - `test` (test) test execution started 3779 | * - `test end` (test) test completed 3780 | * - `hook` (hook) hook execution started 3781 | * - `hook end` (hook) hook complete 3782 | * - `pass` (test) test passed 3783 | * - `fail` (test, err) test failed 3784 | * 3785 | * @api public 3786 | */ 3787 | 3788 | function Runner(suite) { 3789 | var self = this; 3790 | this._globals = []; 3791 | this.suite = suite; 3792 | this.total = suite.total(); 3793 | this.failures = 0; 3794 | this.on('test end', function(test){ self.checkGlobals(test); }); 3795 | this.on('hook end', function(hook){ self.checkGlobals(hook); }); 3796 | this.grep(/.*/); 3797 | this.globals(this.globalProps().concat(['errno'])); 3798 | } 3799 | 3800 | /** 3801 | * Inherit from `EventEmitter.prototype`. 3802 | */ 3803 | 3804 | Runner.prototype = new EventEmitter; 3805 | Runner.prototype.constructor = Runner; 3806 | 3807 | 3808 | /** 3809 | * Run tests with full titles matching `re`. Updates runner.total 3810 | * with number of tests matched. 3811 | * 3812 | * @param {RegExp} re 3813 | * @param {Boolean} invert 3814 | * @return {Runner} for chaining 3815 | * @api public 3816 | */ 3817 | 3818 | Runner.prototype.grep = function(re, invert){ 3819 | debug('grep %s', re); 3820 | this._grep = re; 3821 | this._invert = invert; 3822 | this.total = this.grepTotal(this.suite); 3823 | return this; 3824 | }; 3825 | 3826 | /** 3827 | * Returns the number of tests matching the grep search for the 3828 | * given suite. 3829 | * 3830 | * @param {Suite} suite 3831 | * @return {Number} 3832 | * @api public 3833 | */ 3834 | 3835 | Runner.prototype.grepTotal = function(suite) { 3836 | var self = this; 3837 | var total = 0; 3838 | 3839 | suite.eachTest(function(test){ 3840 | var match = self._grep.test(test.fullTitle()); 3841 | if (self._invert) match = !match; 3842 | if (match) total++; 3843 | }); 3844 | 3845 | return total; 3846 | }; 3847 | 3848 | /** 3849 | * Return a list of global properties. 3850 | * 3851 | * @return {Array} 3852 | * @api private 3853 | */ 3854 | 3855 | Runner.prototype.globalProps = function() { 3856 | var props = utils.keys(global); 3857 | 3858 | // non-enumerables 3859 | for (var i = 0; i < globals.length; ++i) { 3860 | if (~utils.indexOf(props, globals[i])) continue; 3861 | props.push(globals[i]); 3862 | } 3863 | 3864 | return props; 3865 | }; 3866 | 3867 | /** 3868 | * Allow the given `arr` of globals. 3869 | * 3870 | * @param {Array} arr 3871 | * @return {Runner} for chaining 3872 | * @api public 3873 | */ 3874 | 3875 | Runner.prototype.globals = function(arr){ 3876 | if (0 == arguments.length) return this._globals; 3877 | debug('globals %j', arr); 3878 | utils.forEach(arr, function(arr){ 3879 | this._globals.push(arr); 3880 | }, this); 3881 | return this; 3882 | }; 3883 | 3884 | /** 3885 | * Check for global variable leaks. 3886 | * 3887 | * @api private 3888 | */ 3889 | 3890 | Runner.prototype.checkGlobals = function(test){ 3891 | if (this.ignoreLeaks) return; 3892 | var ok = this._globals; 3893 | var globals = this.globalProps(); 3894 | var isNode = process.kill; 3895 | var leaks; 3896 | 3897 | // check length - 2 ('errno' and 'location' globals) 3898 | if (isNode && 1 == ok.length - globals.length) return 3899 | else if (2 == ok.length - globals.length) return; 3900 | 3901 | leaks = filterLeaks(ok, globals); 3902 | this._globals = this._globals.concat(leaks); 3903 | 3904 | if (leaks.length > 1) { 3905 | this.fail(test, new Error('global leaks detected: ' + leaks.join(', ') + '')); 3906 | } else if (leaks.length) { 3907 | this.fail(test, new Error('global leak detected: ' + leaks[0])); 3908 | } 3909 | }; 3910 | 3911 | /** 3912 | * Fail the given `test`. 3913 | * 3914 | * @param {Test} test 3915 | * @param {Error} err 3916 | * @api private 3917 | */ 3918 | 3919 | Runner.prototype.fail = function(test, err){ 3920 | ++this.failures; 3921 | test.state = 'failed'; 3922 | 3923 | if ('string' == typeof err) { 3924 | err = new Error('the string "' + err + '" was thrown, throw an Error :)'); 3925 | } 3926 | 3927 | this.emit('fail', test, err); 3928 | }; 3929 | 3930 | /** 3931 | * Fail the given `hook` with `err`. 3932 | * 3933 | * Hook failures (currently) hard-end due 3934 | * to that fact that a failing hook will 3935 | * surely cause subsequent tests to fail, 3936 | * causing jumbled reporting. 3937 | * 3938 | * @param {Hook} hook 3939 | * @param {Error} err 3940 | * @api private 3941 | */ 3942 | 3943 | Runner.prototype.failHook = function(hook, err){ 3944 | this.fail(hook, err); 3945 | this.emit('end'); 3946 | }; 3947 | 3948 | /** 3949 | * Run hook `name` callbacks and then invoke `fn()`. 3950 | * 3951 | * @param {String} name 3952 | * @param {Function} function 3953 | * @api private 3954 | */ 3955 | 3956 | Runner.prototype.hook = function(name, fn){ 3957 | var suite = this.suite 3958 | , hooks = suite['_' + name] 3959 | , self = this 3960 | , timer; 3961 | 3962 | function next(i) { 3963 | var hook = hooks[i]; 3964 | if (!hook) return fn(); 3965 | self.currentRunnable = hook; 3966 | 3967 | self.emit('hook', hook); 3968 | 3969 | hook.on('error', function(err){ 3970 | self.failHook(hook, err); 3971 | }); 3972 | 3973 | hook.run(function(err){ 3974 | hook.removeAllListeners('error'); 3975 | var testError = hook.error(); 3976 | if (testError) self.fail(self.test, testError); 3977 | if (err) return self.failHook(hook, err); 3978 | self.emit('hook end', hook); 3979 | next(++i); 3980 | }); 3981 | } 3982 | 3983 | process.nextTick(function(){ 3984 | next(0); 3985 | }); 3986 | }; 3987 | 3988 | /** 3989 | * Run hook `name` for the given array of `suites` 3990 | * in order, and callback `fn(err)`. 3991 | * 3992 | * @param {String} name 3993 | * @param {Array} suites 3994 | * @param {Function} fn 3995 | * @api private 3996 | */ 3997 | 3998 | Runner.prototype.hooks = function(name, suites, fn){ 3999 | var self = this 4000 | , orig = this.suite; 4001 | 4002 | function next(suite) { 4003 | self.suite = suite; 4004 | 4005 | if (!suite) { 4006 | self.suite = orig; 4007 | return fn(); 4008 | } 4009 | 4010 | self.hook(name, function(err){ 4011 | if (err) { 4012 | self.suite = orig; 4013 | return fn(err); 4014 | } 4015 | 4016 | next(suites.pop()); 4017 | }); 4018 | } 4019 | 4020 | next(suites.pop()); 4021 | }; 4022 | 4023 | /** 4024 | * Run hooks from the top level down. 4025 | * 4026 | * @param {String} name 4027 | * @param {Function} fn 4028 | * @api private 4029 | */ 4030 | 4031 | Runner.prototype.hookUp = function(name, fn){ 4032 | var suites = [this.suite].concat(this.parents()).reverse(); 4033 | this.hooks(name, suites, fn); 4034 | }; 4035 | 4036 | /** 4037 | * Run hooks from the bottom up. 4038 | * 4039 | * @param {String} name 4040 | * @param {Function} fn 4041 | * @api private 4042 | */ 4043 | 4044 | Runner.prototype.hookDown = function(name, fn){ 4045 | var suites = [this.suite].concat(this.parents()); 4046 | this.hooks(name, suites, fn); 4047 | }; 4048 | 4049 | /** 4050 | * Return an array of parent Suites from 4051 | * closest to furthest. 4052 | * 4053 | * @return {Array} 4054 | * @api private 4055 | */ 4056 | 4057 | Runner.prototype.parents = function(){ 4058 | var suite = this.suite 4059 | , suites = []; 4060 | while (suite = suite.parent) suites.push(suite); 4061 | return suites; 4062 | }; 4063 | 4064 | /** 4065 | * Run the current test and callback `fn(err)`. 4066 | * 4067 | * @param {Function} fn 4068 | * @api private 4069 | */ 4070 | 4071 | Runner.prototype.runTest = function(fn){ 4072 | var test = this.test 4073 | , self = this; 4074 | 4075 | if (this.asyncOnly) test.asyncOnly = true; 4076 | 4077 | try { 4078 | test.on('error', function(err){ 4079 | self.fail(test, err); 4080 | }); 4081 | test.run(fn); 4082 | } catch (err) { 4083 | fn(err); 4084 | } 4085 | }; 4086 | 4087 | /** 4088 | * Run tests in the given `suite` and invoke 4089 | * the callback `fn()` when complete. 4090 | * 4091 | * @param {Suite} suite 4092 | * @param {Function} fn 4093 | * @api private 4094 | */ 4095 | 4096 | Runner.prototype.runTests = function(suite, fn){ 4097 | var self = this 4098 | , tests = suite.tests.slice() 4099 | , test; 4100 | 4101 | function next(err) { 4102 | // if we bail after first err 4103 | if (self.failures && suite._bail) return fn(); 4104 | 4105 | // next test 4106 | test = tests.shift(); 4107 | 4108 | // all done 4109 | if (!test) return fn(); 4110 | 4111 | // grep 4112 | var match = self._grep.test(test.fullTitle()); 4113 | if (self._invert) match = !match; 4114 | if (!match) return next(); 4115 | 4116 | // pending 4117 | if (test.pending) { 4118 | self.emit('pending', test); 4119 | self.emit('test end', test); 4120 | return next(); 4121 | } 4122 | 4123 | // execute test and hook(s) 4124 | self.emit('test', self.test = test); 4125 | self.hookDown('beforeEach', function(){ 4126 | self.currentRunnable = self.test; 4127 | self.runTest(function(err){ 4128 | test = self.test; 4129 | 4130 | if (err) { 4131 | self.fail(test, err); 4132 | self.emit('test end', test); 4133 | return self.hookUp('afterEach', next); 4134 | } 4135 | 4136 | test.state = 'passed'; 4137 | self.emit('pass', test); 4138 | self.emit('test end', test); 4139 | self.hookUp('afterEach', next); 4140 | }); 4141 | }); 4142 | } 4143 | 4144 | this.next = next; 4145 | next(); 4146 | }; 4147 | 4148 | /** 4149 | * Run the given `suite` and invoke the 4150 | * callback `fn()` when complete. 4151 | * 4152 | * @param {Suite} suite 4153 | * @param {Function} fn 4154 | * @api private 4155 | */ 4156 | 4157 | Runner.prototype.runSuite = function(suite, fn){ 4158 | var total = this.grepTotal(suite) 4159 | , self = this 4160 | , i = 0; 4161 | 4162 | debug('run suite %s', suite.fullTitle()); 4163 | 4164 | if (!total) return fn(); 4165 | 4166 | this.emit('suite', this.suite = suite); 4167 | 4168 | function next() { 4169 | var curr = suite.suites[i++]; 4170 | if (!curr) return done(); 4171 | self.runSuite(curr, next); 4172 | } 4173 | 4174 | function done() { 4175 | self.suite = suite; 4176 | self.hook('afterAll', function(){ 4177 | self.emit('suite end', suite); 4178 | fn(); 4179 | }); 4180 | } 4181 | 4182 | this.hook('beforeAll', function(){ 4183 | self.runTests(suite, next); 4184 | }); 4185 | }; 4186 | 4187 | /** 4188 | * Handle uncaught exceptions. 4189 | * 4190 | * @param {Error} err 4191 | * @api private 4192 | */ 4193 | 4194 | Runner.prototype.uncaught = function(err){ 4195 | debug('uncaught exception %s', err.message); 4196 | var runnable = this.currentRunnable; 4197 | if (!runnable || 'failed' == runnable.state) return; 4198 | runnable.clearTimeout(); 4199 | err.uncaught = true; 4200 | this.fail(runnable, err); 4201 | 4202 | // recover from test 4203 | if ('test' == runnable.type) { 4204 | this.emit('test end', runnable); 4205 | this.hookUp('afterEach', this.next); 4206 | return; 4207 | } 4208 | 4209 | // bail on hooks 4210 | this.emit('end'); 4211 | }; 4212 | 4213 | /** 4214 | * Run the root suite and invoke `fn(failures)` 4215 | * on completion. 4216 | * 4217 | * @param {Function} fn 4218 | * @return {Runner} for chaining 4219 | * @api public 4220 | */ 4221 | 4222 | Runner.prototype.run = function(fn){ 4223 | var self = this 4224 | , fn = fn || function(){}; 4225 | 4226 | debug('start'); 4227 | 4228 | // callback 4229 | this.on('end', function(){ 4230 | debug('end'); 4231 | process.removeListener('uncaughtException', function(err){ 4232 | self.uncaught(err); 4233 | }); 4234 | fn(self.failures); 4235 | }); 4236 | 4237 | // run suites 4238 | this.emit('start'); 4239 | this.runSuite(this.suite, function(){ 4240 | debug('finished running'); 4241 | self.emit('end'); 4242 | }); 4243 | 4244 | // uncaught exception 4245 | process.on('uncaughtException', function(err){ 4246 | self.uncaught(err); 4247 | }); 4248 | 4249 | return this; 4250 | }; 4251 | 4252 | /** 4253 | * Filter leaks with the given globals flagged as `ok`. 4254 | * 4255 | * @param {Array} ok 4256 | * @param {Array} globals 4257 | * @return {Array} 4258 | * @api private 4259 | */ 4260 | 4261 | function filterLeaks(ok, globals) { 4262 | return filter(globals, function(key){ 4263 | var matched = filter(ok, function(ok){ 4264 | if (~ok.indexOf('*')) return 0 == key.indexOf(ok.split('*')[0]); 4265 | // Opera and IE expose global variables for HTML element IDs (issue #243) 4266 | if (/^mocha-/.test(key)) return true; 4267 | return key == ok; 4268 | }); 4269 | return matched.length == 0 && (!global.navigator || 'onerror' !== key); 4270 | }); 4271 | } 4272 | 4273 | }); // module: runner.js 4274 | 4275 | require.register("suite.js", function(module, exports, require){ 4276 | 4277 | /** 4278 | * Module dependencies. 4279 | */ 4280 | 4281 | var EventEmitter = require('browser/events').EventEmitter 4282 | , debug = require('browser/debug')('mocha:suite') 4283 | , milliseconds = require('./ms') 4284 | , utils = require('./utils') 4285 | , Hook = require('./hook'); 4286 | 4287 | /** 4288 | * Expose `Suite`. 4289 | */ 4290 | 4291 | exports = module.exports = Suite; 4292 | 4293 | /** 4294 | * Create a new `Suite` with the given `title` 4295 | * and parent `Suite`. When a suite with the 4296 | * same title is already present, that suite 4297 | * is returned to provide nicer reporter 4298 | * and more flexible meta-testing. 4299 | * 4300 | * @param {Suite} parent 4301 | * @param {String} title 4302 | * @return {Suite} 4303 | * @api public 4304 | */ 4305 | 4306 | exports.create = function(parent, title){ 4307 | var suite = new Suite(title, parent.ctx); 4308 | suite.parent = parent; 4309 | if (parent.pending) suite.pending = true; 4310 | title = suite.fullTitle(); 4311 | parent.addSuite(suite); 4312 | return suite; 4313 | }; 4314 | 4315 | /** 4316 | * Initialize a new `Suite` with the given 4317 | * `title` and `ctx`. 4318 | * 4319 | * @param {String} title 4320 | * @param {Context} ctx 4321 | * @api private 4322 | */ 4323 | 4324 | function Suite(title, ctx) { 4325 | this.title = title; 4326 | this.ctx = ctx; 4327 | this.suites = []; 4328 | this.tests = []; 4329 | this.pending = false; 4330 | this._beforeEach = []; 4331 | this._beforeAll = []; 4332 | this._afterEach = []; 4333 | this._afterAll = []; 4334 | this.root = !title; 4335 | this._timeout = 2000; 4336 | this._slow = 75; 4337 | this._bail = false; 4338 | } 4339 | 4340 | /** 4341 | * Inherit from `EventEmitter.prototype`. 4342 | */ 4343 | 4344 | Suite.prototype = new EventEmitter; 4345 | Suite.prototype.constructor = Suite; 4346 | 4347 | 4348 | /** 4349 | * Return a clone of this `Suite`. 4350 | * 4351 | * @return {Suite} 4352 | * @api private 4353 | */ 4354 | 4355 | Suite.prototype.clone = function(){ 4356 | var suite = new Suite(this.title); 4357 | debug('clone'); 4358 | suite.ctx = this.ctx; 4359 | suite.timeout(this.timeout()); 4360 | suite.slow(this.slow()); 4361 | suite.bail(this.bail()); 4362 | return suite; 4363 | }; 4364 | 4365 | /** 4366 | * Set timeout `ms` or short-hand such as "2s". 4367 | * 4368 | * @param {Number|String} ms 4369 | * @return {Suite|Number} for chaining 4370 | * @api private 4371 | */ 4372 | 4373 | Suite.prototype.timeout = function(ms){ 4374 | if (0 == arguments.length) return this._timeout; 4375 | if ('string' == typeof ms) ms = milliseconds(ms); 4376 | debug('timeout %d', ms); 4377 | this._timeout = parseInt(ms, 10); 4378 | return this; 4379 | }; 4380 | 4381 | /** 4382 | * Set slow `ms` or short-hand such as "2s". 4383 | * 4384 | * @param {Number|String} ms 4385 | * @return {Suite|Number} for chaining 4386 | * @api private 4387 | */ 4388 | 4389 | Suite.prototype.slow = function(ms){ 4390 | if (0 === arguments.length) return this._slow; 4391 | if ('string' == typeof ms) ms = milliseconds(ms); 4392 | debug('slow %d', ms); 4393 | this._slow = ms; 4394 | return this; 4395 | }; 4396 | 4397 | /** 4398 | * Sets whether to bail after first error. 4399 | * 4400 | * @parma {Boolean} bail 4401 | * @return {Suite|Number} for chaining 4402 | * @api private 4403 | */ 4404 | 4405 | Suite.prototype.bail = function(bail){ 4406 | if (0 == arguments.length) return this._bail; 4407 | debug('bail %s', bail); 4408 | this._bail = bail; 4409 | return this; 4410 | }; 4411 | 4412 | /** 4413 | * Run `fn(test[, done])` before running tests. 4414 | * 4415 | * @param {Function} fn 4416 | * @return {Suite} for chaining 4417 | * @api private 4418 | */ 4419 | 4420 | Suite.prototype.beforeAll = function(fn){ 4421 | if (this.pending) return this; 4422 | var hook = new Hook('"before all" hook', fn); 4423 | hook.parent = this; 4424 | hook.timeout(this.timeout()); 4425 | hook.slow(this.slow()); 4426 | hook.ctx = this.ctx; 4427 | this._beforeAll.push(hook); 4428 | this.emit('beforeAll', hook); 4429 | return this; 4430 | }; 4431 | 4432 | /** 4433 | * Run `fn(test[, done])` after running tests. 4434 | * 4435 | * @param {Function} fn 4436 | * @return {Suite} for chaining 4437 | * @api private 4438 | */ 4439 | 4440 | Suite.prototype.afterAll = function(fn){ 4441 | if (this.pending) return this; 4442 | var hook = new Hook('"after all" hook', fn); 4443 | hook.parent = this; 4444 | hook.timeout(this.timeout()); 4445 | hook.slow(this.slow()); 4446 | hook.ctx = this.ctx; 4447 | this._afterAll.push(hook); 4448 | this.emit('afterAll', hook); 4449 | return this; 4450 | }; 4451 | 4452 | /** 4453 | * Run `fn(test[, done])` before each test case. 4454 | * 4455 | * @param {Function} fn 4456 | * @return {Suite} for chaining 4457 | * @api private 4458 | */ 4459 | 4460 | Suite.prototype.beforeEach = function(fn){ 4461 | if (this.pending) return this; 4462 | var hook = new Hook('"before each" hook', fn); 4463 | hook.parent = this; 4464 | hook.timeout(this.timeout()); 4465 | hook.slow(this.slow()); 4466 | hook.ctx = this.ctx; 4467 | this._beforeEach.push(hook); 4468 | this.emit('beforeEach', hook); 4469 | return this; 4470 | }; 4471 | 4472 | /** 4473 | * Run `fn(test[, done])` after each test case. 4474 | * 4475 | * @param {Function} fn 4476 | * @return {Suite} for chaining 4477 | * @api private 4478 | */ 4479 | 4480 | Suite.prototype.afterEach = function(fn){ 4481 | if (this.pending) return this; 4482 | var hook = new Hook('"after each" hook', fn); 4483 | hook.parent = this; 4484 | hook.timeout(this.timeout()); 4485 | hook.slow(this.slow()); 4486 | hook.ctx = this.ctx; 4487 | this._afterEach.push(hook); 4488 | this.emit('afterEach', hook); 4489 | return this; 4490 | }; 4491 | 4492 | /** 4493 | * Add a test `suite`. 4494 | * 4495 | * @param {Suite} suite 4496 | * @return {Suite} for chaining 4497 | * @api private 4498 | */ 4499 | 4500 | Suite.prototype.addSuite = function(suite){ 4501 | suite.parent = this; 4502 | suite.timeout(this.timeout()); 4503 | suite.slow(this.slow()); 4504 | suite.bail(this.bail()); 4505 | this.suites.push(suite); 4506 | this.emit('suite', suite); 4507 | return this; 4508 | }; 4509 | 4510 | /** 4511 | * Add a `test` to this suite. 4512 | * 4513 | * @param {Test} test 4514 | * @return {Suite} for chaining 4515 | * @api private 4516 | */ 4517 | 4518 | Suite.prototype.addTest = function(test){ 4519 | test.parent = this; 4520 | test.timeout(this.timeout()); 4521 | test.slow(this.slow()); 4522 | test.ctx = this.ctx; 4523 | this.tests.push(test); 4524 | this.emit('test', test); 4525 | return this; 4526 | }; 4527 | 4528 | /** 4529 | * Return the full title generated by recursively 4530 | * concatenating the parent's full title. 4531 | * 4532 | * @return {String} 4533 | * @api public 4534 | */ 4535 | 4536 | Suite.prototype.fullTitle = function(){ 4537 | if (this.parent) { 4538 | var full = this.parent.fullTitle(); 4539 | if (full) return full + ' ' + this.title; 4540 | } 4541 | return this.title; 4542 | }; 4543 | 4544 | /** 4545 | * Return the total number of tests. 4546 | * 4547 | * @return {Number} 4548 | * @api public 4549 | */ 4550 | 4551 | Suite.prototype.total = function(){ 4552 | return utils.reduce(this.suites, function(sum, suite){ 4553 | return sum + suite.total(); 4554 | }, 0) + this.tests.length; 4555 | }; 4556 | 4557 | /** 4558 | * Iterates through each suite recursively to find 4559 | * all tests. Applies a function in the format 4560 | * `fn(test)`. 4561 | * 4562 | * @param {Function} fn 4563 | * @return {Suite} 4564 | * @api private 4565 | */ 4566 | 4567 | Suite.prototype.eachTest = function(fn){ 4568 | utils.forEach(this.tests, fn); 4569 | utils.forEach(this.suites, function(suite){ 4570 | suite.eachTest(fn); 4571 | }); 4572 | return this; 4573 | }; 4574 | 4575 | }); // module: suite.js 4576 | 4577 | require.register("test.js", function(module, exports, require){ 4578 | 4579 | /** 4580 | * Module dependencies. 4581 | */ 4582 | 4583 | var Runnable = require('./runnable'); 4584 | 4585 | /** 4586 | * Expose `Test`. 4587 | */ 4588 | 4589 | module.exports = Test; 4590 | 4591 | /** 4592 | * Initialize a new `Test` with the given `title` and callback `fn`. 4593 | * 4594 | * @param {String} title 4595 | * @param {Function} fn 4596 | * @api private 4597 | */ 4598 | 4599 | function Test(title, fn) { 4600 | Runnable.call(this, title, fn); 4601 | this.pending = !fn; 4602 | this.type = 'test'; 4603 | } 4604 | 4605 | /** 4606 | * Inherit from `Runnable.prototype`. 4607 | */ 4608 | 4609 | Test.prototype = new Runnable; 4610 | Test.prototype.constructor = Test; 4611 | 4612 | 4613 | }); // module: test.js 4614 | 4615 | require.register("utils.js", function(module, exports, require){ 4616 | 4617 | /** 4618 | * Module dependencies. 4619 | */ 4620 | 4621 | var fs = require('browser/fs') 4622 | , path = require('browser/path') 4623 | , join = path.join 4624 | , debug = require('browser/debug')('mocha:watch'); 4625 | 4626 | /** 4627 | * Ignored directories. 4628 | */ 4629 | 4630 | var ignore = ['node_modules', '.git']; 4631 | 4632 | /** 4633 | * Escape special characters in the given string of html. 4634 | * 4635 | * @param {String} html 4636 | * @return {String} 4637 | * @api private 4638 | */ 4639 | 4640 | exports.escape = function(html){ 4641 | return String(html) 4642 | .replace(/&/g, '&') 4643 | .replace(/"/g, '"') 4644 | .replace(//g, '>'); 4646 | }; 4647 | 4648 | /** 4649 | * Array#forEach (<=IE8) 4650 | * 4651 | * @param {Array} array 4652 | * @param {Function} fn 4653 | * @param {Object} scope 4654 | * @api private 4655 | */ 4656 | 4657 | exports.forEach = function(arr, fn, scope){ 4658 | for (var i = 0, l = arr.length; i < l; i++) 4659 | fn.call(scope, arr[i], i); 4660 | }; 4661 | 4662 | /** 4663 | * Array#indexOf (<=IE8) 4664 | * 4665 | * @parma {Array} arr 4666 | * @param {Object} obj to find index of 4667 | * @param {Number} start 4668 | * @api private 4669 | */ 4670 | 4671 | exports.indexOf = function(arr, obj, start){ 4672 | for (var i = start || 0, l = arr.length; i < l; i++) { 4673 | if (arr[i] === obj) 4674 | return i; 4675 | } 4676 | return -1; 4677 | }; 4678 | 4679 | /** 4680 | * Array#reduce (<=IE8) 4681 | * 4682 | * @param {Array} array 4683 | * @param {Function} fn 4684 | * @param {Object} initial value 4685 | * @api private 4686 | */ 4687 | 4688 | exports.reduce = function(arr, fn, val){ 4689 | var rval = val; 4690 | 4691 | for (var i = 0, l = arr.length; i < l; i++) { 4692 | rval = fn(rval, arr[i], i, arr); 4693 | } 4694 | 4695 | return rval; 4696 | }; 4697 | 4698 | /** 4699 | * Array#filter (<=IE8) 4700 | * 4701 | * @param {Array} array 4702 | * @param {Function} fn 4703 | * @api private 4704 | */ 4705 | 4706 | exports.filter = function(arr, fn){ 4707 | var ret = []; 4708 | 4709 | for (var i = 0, l = arr.length; i < l; i++) { 4710 | var val = arr[i]; 4711 | if (fn(val, i, arr)) ret.push(val); 4712 | } 4713 | 4714 | return ret; 4715 | }; 4716 | 4717 | /** 4718 | * Object.keys (<=IE8) 4719 | * 4720 | * @param {Object} obj 4721 | * @return {Array} keys 4722 | * @api private 4723 | */ 4724 | 4725 | exports.keys = Object.keys || function(obj) { 4726 | var keys = [] 4727 | , has = Object.prototype.hasOwnProperty // for `window` on <=IE8 4728 | 4729 | for (var key in obj) { 4730 | if (has.call(obj, key)) { 4731 | keys.push(key); 4732 | } 4733 | } 4734 | 4735 | return keys; 4736 | }; 4737 | 4738 | /** 4739 | * Watch the given `files` for changes 4740 | * and invoke `fn(file)` on modification. 4741 | * 4742 | * @param {Array} files 4743 | * @param {Function} fn 4744 | * @api private 4745 | */ 4746 | 4747 | exports.watch = function(files, fn){ 4748 | var options = { interval: 100 }; 4749 | files.forEach(function(file){ 4750 | debug('file %s', file); 4751 | fs.watchFile(file, options, function(curr, prev){ 4752 | if (prev.mtime < curr.mtime) fn(file); 4753 | }); 4754 | }); 4755 | }; 4756 | 4757 | /** 4758 | * Ignored files. 4759 | */ 4760 | 4761 | function ignored(path){ 4762 | return !~ignore.indexOf(path); 4763 | } 4764 | 4765 | /** 4766 | * Lookup files in the given `dir`. 4767 | * 4768 | * @return {Array} 4769 | * @api private 4770 | */ 4771 | 4772 | exports.files = function(dir, ret){ 4773 | ret = ret || []; 4774 | 4775 | fs.readdirSync(dir) 4776 | .filter(ignored) 4777 | .forEach(function(path){ 4778 | path = join(dir, path); 4779 | if (fs.statSync(path).isDirectory()) { 4780 | exports.files(path, ret); 4781 | } else if (path.match(/\.(js|coffee)$/)) { 4782 | ret.push(path); 4783 | } 4784 | }); 4785 | 4786 | return ret; 4787 | }; 4788 | 4789 | /** 4790 | * Compute a slug from the given `str`. 4791 | * 4792 | * @param {String} str 4793 | * @return {String} 4794 | * @api private 4795 | */ 4796 | 4797 | exports.slug = function(str){ 4798 | return str 4799 | .toLowerCase() 4800 | .replace(/ +/g, '-') 4801 | .replace(/[^-\w]/g, ''); 4802 | }; 4803 | 4804 | /** 4805 | * Strip the function definition from `str`, 4806 | * and re-indent for pre whitespace. 4807 | */ 4808 | 4809 | exports.clean = function(str) { 4810 | str = str 4811 | .replace(/^function *\(.*\) *{/, '') 4812 | .replace(/\s+\}$/, ''); 4813 | 4814 | var spaces = str.match(/^\n?( *)/)[1].length 4815 | , re = new RegExp('^ {' + spaces + '}', 'gm'); 4816 | 4817 | str = str.replace(re, ''); 4818 | 4819 | return exports.trim(str); 4820 | }; 4821 | 4822 | /** 4823 | * Escape regular expression characters in `str`. 4824 | * 4825 | * @param {String} str 4826 | * @return {String} 4827 | * @api private 4828 | */ 4829 | 4830 | exports.escapeRegexp = function(str){ 4831 | return str.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&"); 4832 | }; 4833 | 4834 | /** 4835 | * Trim the given `str`. 4836 | * 4837 | * @param {String} str 4838 | * @return {String} 4839 | * @api private 4840 | */ 4841 | 4842 | exports.trim = function(str){ 4843 | return str.replace(/^\s+|\s+$/g, ''); 4844 | }; 4845 | 4846 | /** 4847 | * Parse the given `qs`. 4848 | * 4849 | * @param {String} qs 4850 | * @return {Object} 4851 | * @api private 4852 | */ 4853 | 4854 | exports.parseQuery = function(qs){ 4855 | return exports.reduce(qs.replace('?', '').split('&'), function(obj, pair){ 4856 | var i = pair.indexOf('=') 4857 | , key = pair.slice(0, i) 4858 | , val = pair.slice(++i); 4859 | 4860 | obj[key] = decodeURIComponent(val); 4861 | return obj; 4862 | }, {}); 4863 | }; 4864 | 4865 | /** 4866 | * Highlight the given string of `js`. 4867 | * 4868 | * @param {String} js 4869 | * @return {String} 4870 | * @api private 4871 | */ 4872 | 4873 | function highlight(js) { 4874 | return js 4875 | .replace(//g, '>') 4877 | .replace(/\/\/(.*)/gm, '//$1') 4878 | .replace(/('.*?')/gm, '$1') 4879 | .replace(/(\d+\.\d+)/gm, '$1') 4880 | .replace(/(\d+)/gm, '$1') 4881 | .replace(/\bnew *(\w+)/gm, 'new $1') 4882 | .replace(/\b(function|new|throw|return|var|if|else)\b/gm, '$1') 4883 | } 4884 | 4885 | /** 4886 | * Highlight the contents of tag `name`. 4887 | * 4888 | * @param {String} name 4889 | * @api private 4890 | */ 4891 | 4892 | exports.highlightTags = function(name) { 4893 | var code = document.getElementsByTagName(name); 4894 | for (var i = 0, len = code.length; i < len; ++i) { 4895 | code[i].innerHTML = highlight(code[i].innerHTML); 4896 | } 4897 | }; 4898 | 4899 | }); // module: utils.js 4900 | /** 4901 | * Node shims. 4902 | * 4903 | * These are meant only to allow 4904 | * mocha.js to run untouched, not 4905 | * to allow running node code in 4906 | * the browser. 4907 | */ 4908 | 4909 | process = {}; 4910 | process.exit = function(status){}; 4911 | process.stdout = {}; 4912 | global = window; 4913 | 4914 | /** 4915 | * next tick implementation. 4916 | */ 4917 | 4918 | process.nextTick = (function(){ 4919 | // postMessage behaves badly on IE8 4920 | if (window.ActiveXObject || !window.postMessage) { 4921 | return function(fn){ fn() }; 4922 | } 4923 | 4924 | // based on setZeroTimeout by David Baron 4925 | // - http://dbaron.org/log/20100309-faster-timeouts 4926 | var timeouts = [] 4927 | , name = 'mocha-zero-timeout' 4928 | 4929 | window.addEventListener('message', function(e){ 4930 | if (e.source == window && e.data == name) { 4931 | if (e.stopPropagation) e.stopPropagation(); 4932 | if (timeouts.length) timeouts.shift()(); 4933 | } 4934 | }, true); 4935 | 4936 | return function(fn){ 4937 | timeouts.push(fn); 4938 | window.postMessage(name, '*'); 4939 | } 4940 | })(); 4941 | 4942 | /** 4943 | * Remove uncaughtException listener. 4944 | */ 4945 | 4946 | process.removeListener = function(e){ 4947 | if ('uncaughtException' == e) { 4948 | window.onerror = null; 4949 | } 4950 | }; 4951 | 4952 | /** 4953 | * Implements uncaughtException listener. 4954 | */ 4955 | 4956 | process.on = function(e, fn){ 4957 | if ('uncaughtException' == e) { 4958 | window.onerror = function(err, url, line){ 4959 | fn(new Error(err + ' (' + url + ':' + line + ')')); 4960 | }; 4961 | } 4962 | }; 4963 | 4964 | // boot 4965 | ;(function(){ 4966 | 4967 | /** 4968 | * Expose mocha. 4969 | */ 4970 | 4971 | var Mocha = window.Mocha = require('mocha'), 4972 | mocha = window.mocha = new Mocha({ reporter: 'html' }); 4973 | 4974 | /** 4975 | * Override ui to ensure that the ui functions are initialized. 4976 | * Normally this would happen in Mocha.prototype.loadFiles. 4977 | */ 4978 | 4979 | mocha.ui = function(ui){ 4980 | Mocha.prototype.ui.call(this, ui); 4981 | this.suite.emit('pre-require', window, null, this); 4982 | return this; 4983 | }; 4984 | 4985 | /** 4986 | * Setup mocha with the given setting options. 4987 | */ 4988 | 4989 | mocha.setup = function(opts){ 4990 | if ('string' == typeof opts) opts = { ui: opts }; 4991 | for (var opt in opts) this[opt](opts[opt]); 4992 | return this; 4993 | }; 4994 | 4995 | /** 4996 | * Run mocha, returning the Runner. 4997 | */ 4998 | 4999 | mocha.run = function(fn){ 5000 | var options = mocha.options; 5001 | mocha.globals('location'); 5002 | 5003 | var query = Mocha.utils.parseQuery(window.location.search || ''); 5004 | if (query.grep) mocha.grep(query.grep); 5005 | if (query.invert) mocha.invert(); 5006 | 5007 | return Mocha.prototype.run.call(mocha, function(){ 5008 | Mocha.utils.highlightTags('code'); 5009 | if (fn) fn(); 5010 | }); 5011 | }; 5012 | })(); 5013 | })(); -------------------------------------------------------------------------------- /test/markdown.js: -------------------------------------------------------------------------------- 1 | var constructor = require('..'), 2 | assert = require('assert'); 3 | 4 | 5 | describe("Markdown", function() { 6 | 7 | describe("Basic", function() { 8 | var marc; 9 | beforeEach(function() { 10 | marc = constructor(); 11 | }); 12 | 13 | it('should render html', function() { 14 | console.time('basic'); 15 | var result = marc('I am using __markdown__.'); 16 | console.timeEnd('basic'); 17 | assert.equal(result,'I am using markdown.\n'); 18 | }); 19 | 20 | }); 21 | 22 | describe("Dynamic", function() { 23 | var marc; 24 | beforeEach(function() { 25 | marc = constructor({ 26 | label: 'markdown' 27 | }); 28 | }); 29 | 30 | it('should render html with data', function() { 31 | console.time('dynamic'); 32 | var result = marc('I am using __{{label}}__.', true); 33 | console.timeEnd('dynamic'); 34 | assert.equal(result,'I am using markdown.\n'); 35 | }); 36 | 37 | it('should update rendered html', function(done) { 38 | var expected = 'I am using other.\n'; 39 | var result = marc('I am using __{{label}}__.', function(val) { 40 | if(val === expected) done(); 41 | }); 42 | assert.equal(result, 'I am using markdown.\n'); 43 | marc.set('label', 'other'); 44 | }); 45 | 46 | it('should update html everytime data changes', function(done) { 47 | var i = 0, 48 | expected = 'I am using bredele.\n'; 49 | var result = marc('I am using __{{label}}__.', function(val) { 50 | i++; 51 | if(i === 2 && val === expected) done(); 52 | }); 53 | marc.set('label', 'other'); 54 | marc.set('label', 'bredele'); 55 | }); 56 | 57 | it('should filter markdown', function() { 58 | marc.filter('upper', function(str) { 59 | return str.toUpperCase(); 60 | }); 61 | var result = marc('I am using __{{label} | upper}__.', true); 62 | assert.equal(result,'I am using MARKDOWN.\n'); 63 | }); 64 | 65 | // console.log(marc('__{{>partial}}__.')); 66 | 67 | }); 68 | 69 | 70 | 71 | }); -------------------------------------------------------------------------------- /test/markdown/amy.html: -------------------------------------------------------------------------------- 1 | I am using markdown. 2 | -------------------------------------------------------------------------------- /test/markdown/basic.md: -------------------------------------------------------------------------------- 1 | I am using __markdown__. -------------------------------------------------------------------------------- /test/options.js: -------------------------------------------------------------------------------- 1 | var constructor = require('..'), 2 | assert = require('assert'); 3 | 4 | 5 | describe("Options", function() { 6 | 7 | var marc; 8 | beforeEach(function() { 9 | marc = constructor(); 10 | }); 11 | 12 | it("should have a options handler", function() { 13 | assert.equal(typeof marc.config, 'function'); 14 | }); 15 | 16 | 17 | it("should set and get option", function() { 18 | marc.config('sanitize', true); 19 | assert.equal(marc.config('sanitize'), true); 20 | }); 21 | 22 | it('should set multiple options', function() { 23 | marc.config({ 24 | sanitize: true, 25 | escaped:false 26 | }); 27 | assert.equal(marc.config('sanitize'), true); 28 | assert.equal(marc.config('escaped'), false); 29 | }); 30 | 31 | }); 32 | -------------------------------------------------------------------------------- /test/partials.js: -------------------------------------------------------------------------------- 1 | var constructor = require('..'), 2 | assert = require('assert'); 3 | 4 | 5 | describe("Partials", function() { 6 | var marc; 7 | beforeEach(function() { 8 | marc = constructor(); 9 | }); 10 | 11 | it("should add partials markdown", function() { 12 | assert.equal(typeof marc.partial, 'function'); 13 | }); 14 | 15 | describe("render:", function() { 16 | 17 | it("should render partials", function() { 18 | marc.partial('hello', '__hello world!__'); 19 | var result = marc('This is a partial {> hello }', true); 20 | assert.equal(result, 'This is a partial hello world!\n'); 21 | }); 22 | 23 | it('should render partials with data', function() { 24 | marc.partial('hello', '__{{label}}!__'); 25 | marc.set({ 26 | label: 'hello world', 27 | name: 'hello' 28 | }); 29 | var result = marc('This is the partial {{ name }}:{> hello }', true); 30 | assert.equal(result, 'This is the partial hello:hello world!\n'); 31 | }); 32 | 33 | it('should update partials on changes', function(done) { 34 | marc.partial('hello', '__{{label}}!__'); 35 | var result = marc('This is a partial {> hello }', function(val) { 36 | if(val === 'This is a partial hello world!\n') done(); 37 | }); 38 | marc.set('label', 'hello world'); 39 | }); 40 | 41 | it('should update partials on changes', function(done) { 42 | marc.partial('hello', '__{{name}}!__'); 43 | marc.set('name', 'hello world'); 44 | var result = marc('This is a {{label}} {> hello }', function(val) { 45 | if(val === 'This is a partial hello world!\n') done(); 46 | }); 47 | marc.set('label', 'partial'); 48 | }); 49 | 50 | }); 51 | 52 | 53 | }); 54 | --------------------------------------------------------------------------------
I am using markdown.
I am using markdown with marc!.
I am using markdown with github.
This is a partial: hello world!
') val = val.substring(3, val.length - 5); 44 | return val; 45 | } 46 | }); 47 | } 48 | 49 | 50 | /** 51 | * Generates html from mardown. 52 | * If the second argument is truethy, marc will substitute 53 | * the markdown variables with data. 54 | * example: 55 | * 56 | * marc('hello __world__'); 57 | * marc('hello __{{ label }}__', true); 58 | * marc('hello __{{ label }}__', function(val) { 59 | * //do something 60 | * }); 61 | * 62 | * @param {String} markdown 63 | * @param {Boolean|Function} fn 64 | * @return {String} escaped html 65 | * @api public 66 | */ 67 | 68 | function marc(str, fn) { 69 | var result = marked(str); 70 | if(fn) { 71 | result = store.supplant(partial(result), typeof fn === 'function' ? fn : false); 72 | } 73 | return result; 74 | } 75 | 76 | 77 | /** 78 | * Set data. 79 | * @see http://github.com/bredele/store 80 | * 81 | * @param {String|Object} name n] 82 | * @param {Any} val 83 | * @api public 84 | */ 85 | 86 | marc.set = function(name, val) { 87 | store.set(name, val); 88 | return this; 89 | }; 90 | 91 | 92 | /** 93 | * Get data. 94 | * @see http://github.com/bredele/store 95 | * 96 | * @param {String} name n] 97 | * @return {Any} val 98 | * @api public 99 | */ 100 | 101 | marc.get = function(name) { 102 | return store.get(name); 103 | }; 104 | 105 | 106 | /** 107 | * Add template filters. 108 | * example: 109 | * 110 | * marc.filter('hello', function(str) { 111 | * return 'hello ' + str + '!'; 112 | * }); 113 | * 114 | * @param {String} name 115 | * @param {Function} fn 116 | * @api public 117 | */ 118 | 119 | marc.filter = function(name, fn) { 120 | store.filter(name, fn); 121 | return this; 122 | }; 123 | 124 | 125 | /** 126 | * Set/get markdown options. 127 | * example: 128 | * 129 | * //set 130 | * marc.config('sanitize', true); 131 | * marc.config({ 132 | * sanitize: true, 133 | * gfm: true 134 | * }); 135 | * //get 136 | * marc.config('sanitize'); 137 | * 138 | * @param {String|Object} name 139 | * @param {Any} val 140 | * @api public 141 | */ 142 | 143 | marc.config = function(name, val) { 144 | if(typeof name === 'string') { 145 | if(val) { 146 | var obj = {}; 147 | obj[name] = val; 148 | marked.setOptions(obj); 149 | } else { 150 | return marked.defaults[name]; 151 | } 152 | } else { 153 | marked.setOptions(name); 154 | } 155 | return this; 156 | }; 157 | 158 | 159 | /** 160 | * Add template partial. 161 | * A partial is called everytime there is {> } 162 | * example: 163 | * 164 | * marc.partial('hello', 'hello __{{ label }}__'); 165 | * 166 | * @param {String} name 167 | * @param {String} str 168 | * @api public 169 | */ 170 | 171 | marc.partial = function(name, str) { 172 | partials[name] = str; 173 | }; 174 | 175 | 176 | 177 | return marc; 178 | 179 | }; 180 | 181 | 182 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "marc", 3 | "version": "0.1.0", 4 | "description": "A better markdown parser and compiler", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha" 8 | }, 9 | "bin": { 10 | "marc": "bin/marc" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git://github.com/bredele/marc.git" 15 | }, 16 | "keywords": [ 17 | "markdown", 18 | "parser", 19 | "compiler" 20 | ], 21 | "author": "Olivier Wietrich (http://github.com/bredele)", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/bredele/marc/issues" 25 | }, 26 | "devDependencies": { 27 | "mocha": "~1.17.1" 28 | }, 29 | "dependencies": { 30 | "commander": "~2.1.0", 31 | "datastore": "^1.0.0", 32 | "marked": "~0.3.1", 33 | "read-file-stdin": "0.0.3", 34 | "store-supplant": "0.0.2", 35 | "trim": "0.0.1", 36 | "write-file-stdout": "0.0.2" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/api.js: -------------------------------------------------------------------------------- 1 | var constructor = require('..'), 2 | assert = require('assert'); 3 | 4 | 5 | describe("API", function() { 6 | 7 | describe("constructor", function() { 8 | 9 | it("should be a function", function() { 10 | assert.equal(typeof constructor, 'function'); 11 | }); 12 | 13 | it('should return a function', function() { 14 | assert.equal(typeof constructor(), 'function'); 15 | }); 16 | 17 | }); 18 | 19 | describe("inherits from store", function() { 20 | var marc; 21 | beforeEach(function() { 22 | marc = constructor(); 23 | }); 24 | 25 | it('should mixin store api', function() { 26 | assert.equal(typeof marc.set, 'function'); 27 | assert.equal(typeof marc.get, 'function'); 28 | }); 29 | 30 | it("should set/get data", function() { 31 | marc.set('template', 'marc'); 32 | assert.equal(marc.get('template'), 'marc'); 33 | }); 34 | 35 | }); 36 | 37 | 38 | }); 39 | -------------------------------------------------------------------------------- /test/browser/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Marc 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/browser/marc.js: -------------------------------------------------------------------------------- 1 | var constructor = require('marc'), 2 | assert = require('assert'); 3 | 4 | 5 | describe("API", function() { 6 | 7 | describe("constructor", function() { 8 | 9 | it("should be a function", function() { 10 | assert.equal(typeof constructor, 'function'); 11 | }); 12 | 13 | it('should return a function', function() { 14 | assert.equal(typeof constructor(), 'function'); 15 | }); 16 | 17 | }); 18 | 19 | describe("inherits from store", function() { 20 | var marc; 21 | beforeEach(function() { 22 | marc = constructor(); 23 | }); 24 | 25 | it('should mixin store api', function() { 26 | assert.equal(typeof marc.set, 'function'); 27 | assert.equal(typeof marc.get, 'function'); 28 | }); 29 | 30 | it("should set/get data", function() { 31 | marc.set('template', 'marc'); 32 | assert.equal(marc.get('template'), 'marc'); 33 | }); 34 | 35 | }); 36 | 37 | }); 38 | 39 | 40 | describe("Markdown", function() { 41 | 42 | describe("Basic", function() { 43 | var marc; 44 | beforeEach(function() { 45 | marc = constructor(); 46 | }); 47 | 48 | it('should render html', function() { 49 | var result = marc('I am using __markdown__.'); 50 | assert.equal(result,'I am using markdown.\n'); 51 | }); 52 | 53 | }); 54 | 55 | describe("Dynamic", function() { 56 | var marc; 57 | beforeEach(function() { 58 | marc = constructor({ 59 | label: 'markdown' 60 | }); 61 | }); 62 | 63 | it('should render html with data', function() { 64 | var result = marc('I am using __{{label}}__.', true); 65 | assert.equal(result,'I am using markdown.\n'); 66 | }); 67 | 68 | it('should update rendered html', function(done) { 69 | var expected = 'I am using other.\n'; 70 | var result = marc('I am using __{{label}}__.', function(val) { 71 | if(val === expected) done(); 72 | }); 73 | assert.equal(result, 'I am using markdown.\n'); 74 | marc.set('label', 'other'); 75 | }); 76 | 77 | it('should update html everytime data changes', function(done) { 78 | var i = 0, 79 | expected = 'I am using bredele.\n'; 80 | var result = marc('I am using __{{label}}__.', function(val) { 81 | i++; 82 | if(i === 2 && val === expected) done(); 83 | }); 84 | marc.set('label', 'other'); 85 | marc.set('label', 'bredele'); 86 | }); 87 | 88 | it('should filter markdown', function() { 89 | marc.filter('upper', function(str) { 90 | return str.toUpperCase(); 91 | }); 92 | var result = marc('I am using __{{label} | upper}__.', true); 93 | assert.equal(result,'I am using MARKDOWN.\n'); 94 | }); 95 | 96 | // console.log(marc('__{{>partial}}__.')); 97 | 98 | }); 99 | 100 | }); 101 | 102 | describe("Options", function() { 103 | 104 | var marc; 105 | beforeEach(function() { 106 | marc = constructor(); 107 | }); 108 | 109 | it("should have a options handler", function() { 110 | assert.equal(typeof marc.config, 'function'); 111 | }); 112 | 113 | 114 | it("should set and get option", function() { 115 | marc.config('sanitize', true); 116 | assert.equal(marc.config('sanitize'), true); 117 | }); 118 | 119 | it('should set multiple options', function() { 120 | marc.config({ 121 | sanitize: true, 122 | escaped:false 123 | }); 124 | assert.equal(marc.config('sanitize'), true); 125 | assert.equal(marc.config('escaped'), false); 126 | }); 127 | 128 | }); 129 | 130 | 131 | describe("Partials", function() { 132 | var marc; 133 | beforeEach(function() { 134 | marc = constructor(); 135 | }); 136 | 137 | it("should add partials markdown", function() { 138 | assert.equal(typeof marc.partial, 'function'); 139 | }); 140 | 141 | describe("render:", function() { 142 | 143 | it("should render partials", function() { 144 | marc.partial('hello', '__hello world!__'); 145 | var result = marc('This is a partial {> hello }', true); 146 | assert.equal(result, 'This is a partial hello world!\n'); 147 | }); 148 | 149 | it('should render partials with data', function() { 150 | marc.partial('hello', '__{{label}}!__'); 151 | marc.set({ 152 | label: 'hello world', 153 | name: 'hello' 154 | }); 155 | var result = marc('This is the partial {{ name }}:{> hello }', true); 156 | assert.equal(result, 'This is the partial hello:hello world!\n'); 157 | }); 158 | 159 | it('should update partials on changes', function(done) { 160 | marc.partial('hello', '__{{label}}!__'); 161 | var result = marc('This is a partial {> hello }', function(val) { 162 | if(val === 'This is a partial hello world!\n') done(); 163 | }); 164 | marc.set('label', 'hello world'); 165 | }); 166 | 167 | it('should update partials on changes', function(done) { 168 | marc.partial('hello', '__{{name}}!__'); 169 | marc.set('name', 'hello world'); 170 | var result = marc('This is a {{label}} {> hello }', function(val) { 171 | if(val === 'This is a partial hello world!\n') done(); 172 | }); 173 | marc.set('label', 'partial'); 174 | }); 175 | 176 | }); 177 | 178 | 179 | }); 180 | 181 | 182 | -------------------------------------------------------------------------------- /test/browser/mocha/mocha.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | body { 4 | font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; 5 | padding: 60px 50px; 6 | } 7 | 8 | #mocha ul, #mocha li { 9 | margin: 0; 10 | padding: 0; 11 | } 12 | 13 | #mocha ul { 14 | list-style: none; 15 | } 16 | 17 | #mocha h1, #mocha h2 { 18 | margin: 0; 19 | } 20 | 21 | #mocha h1 { 22 | margin-top: 15px; 23 | font-size: 1em; 24 | font-weight: 200; 25 | } 26 | 27 | #mocha h1 a { 28 | text-decoration: none; 29 | color: inherit; 30 | } 31 | 32 | #mocha h1 a:hover { 33 | text-decoration: underline; 34 | } 35 | 36 | #mocha .suite .suite h1 { 37 | margin-top: 0; 38 | font-size: .8em; 39 | } 40 | 41 | .hidden { 42 | display: none; 43 | } 44 | 45 | #mocha h2 { 46 | font-size: 12px; 47 | font-weight: normal; 48 | cursor: pointer; 49 | } 50 | 51 | #mocha .suite { 52 | margin-left: 15px; 53 | } 54 | 55 | #mocha .test { 56 | margin-left: 15px; 57 | overflow: hidden; 58 | } 59 | 60 | #mocha .test.pending:hover h2::after { 61 | content: '(pending)'; 62 | font-family: arial; 63 | } 64 | 65 | #mocha .test.pass.medium .duration { 66 | background: #C09853; 67 | } 68 | 69 | #mocha .test.pass.slow .duration { 70 | background: #B94A48; 71 | } 72 | 73 | #mocha .test.pass::before { 74 | content: '✓'; 75 | font-size: 12px; 76 | display: block; 77 | float: left; 78 | margin-right: 5px; 79 | color: #00d6b2; 80 | } 81 | 82 | #mocha .test.pass .duration { 83 | font-size: 9px; 84 | margin-left: 5px; 85 | padding: 2px 5px; 86 | color: white; 87 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 88 | -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 89 | box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 90 | -webkit-border-radius: 5px; 91 | -moz-border-radius: 5px; 92 | -ms-border-radius: 5px; 93 | -o-border-radius: 5px; 94 | border-radius: 5px; 95 | } 96 | 97 | #mocha .test.pass.fast .duration { 98 | display: none; 99 | } 100 | 101 | #mocha .test.pending { 102 | color: #0b97c4; 103 | } 104 | 105 | #mocha .test.pending::before { 106 | content: '◦'; 107 | color: #0b97c4; 108 | } 109 | 110 | #mocha .test.fail { 111 | color: #c00; 112 | } 113 | 114 | #mocha .test.fail pre { 115 | color: black; 116 | } 117 | 118 | #mocha .test.fail::before { 119 | content: '✖'; 120 | font-size: 12px; 121 | display: block; 122 | float: left; 123 | margin-right: 5px; 124 | color: #c00; 125 | } 126 | 127 | #mocha .test pre.error { 128 | color: #c00; 129 | max-height: 300px; 130 | overflow: auto; 131 | } 132 | 133 | #mocha .test pre { 134 | display: block; 135 | float: left; 136 | clear: left; 137 | font: 12px/1.5 monaco, monospace; 138 | margin: 5px; 139 | padding: 15px; 140 | border: 1px solid #eee; 141 | border-bottom-color: #ddd; 142 | -webkit-border-radius: 3px; 143 | -webkit-box-shadow: 0 1px 3px #eee; 144 | -moz-border-radius: 3px; 145 | -moz-box-shadow: 0 1px 3px #eee; 146 | } 147 | 148 | #mocha .test h2 { 149 | position: relative; 150 | } 151 | 152 | #mocha .test a.replay { 153 | position: absolute; 154 | top: 3px; 155 | right: 0; 156 | text-decoration: none; 157 | vertical-align: middle; 158 | display: block; 159 | width: 15px; 160 | height: 15px; 161 | line-height: 15px; 162 | text-align: center; 163 | background: #eee; 164 | font-size: 15px; 165 | -moz-border-radius: 15px; 166 | border-radius: 15px; 167 | -webkit-transition: opacity 200ms; 168 | -moz-transition: opacity 200ms; 169 | transition: opacity 200ms; 170 | opacity: 0.3; 171 | color: #888; 172 | } 173 | 174 | #mocha .test:hover a.replay { 175 | opacity: 1; 176 | } 177 | 178 | #mocha-report.pass .test.fail { 179 | display: none; 180 | } 181 | 182 | #mocha-report.fail .test.pass { 183 | display: none; 184 | } 185 | 186 | #mocha-error { 187 | color: #c00; 188 | font-size: 1.5 em; 189 | font-weight: 100; 190 | letter-spacing: 1px; 191 | } 192 | 193 | #mocha-stats { 194 | position: fixed; 195 | top: 15px; 196 | right: 10px; 197 | font-size: 12px; 198 | margin: 0; 199 | color: #888; 200 | } 201 | 202 | #mocha-stats .progress { 203 | float: right; 204 | padding-top: 0; 205 | } 206 | 207 | #mocha-stats em { 208 | color: black; 209 | } 210 | 211 | #mocha-stats a { 212 | text-decoration: none; 213 | color: inherit; 214 | } 215 | 216 | #mocha-stats a:hover { 217 | border-bottom: 1px solid #eee; 218 | } 219 | 220 | #mocha-stats li { 221 | display: inline-block; 222 | margin: 0 5px; 223 | list-style: none; 224 | padding-top: 11px; 225 | } 226 | 227 | #mocha-stats canvas { 228 | width: 40px; 229 | height: 40px; 230 | } 231 | 232 | code .comment { color: #ddd } 233 | code .init { color: #2F6FAD } 234 | code .string { color: #5890AD } 235 | code .keyword { color: #8A6343 } 236 | code .number { color: #2F6FAD } 237 | 238 | @media screen and (max-device-width: 480px) { 239 | body { 240 | padding: 60px 0px; 241 | } 242 | 243 | #stats { 244 | position: absolute; 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /test/browser/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 public 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 or get 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 | if (0 == arguments.length) return this._runnable; 419 | this.test = this._runnable = runnable; 420 | return this; 421 | }; 422 | 423 | /** 424 | * Set test timeout `ms`. 425 | * 426 | * @param {Number} ms 427 | * @return {Context} self 428 | * @api private 429 | */ 430 | 431 | Context.prototype.timeout = function(ms){ 432 | this.runnable().timeout(ms); 433 | return this; 434 | }; 435 | 436 | /** 437 | * Set test slowness threshold `ms`. 438 | * 439 | * @param {Number} ms 440 | * @return {Context} self 441 | * @api private 442 | */ 443 | 444 | Context.prototype.slow = function(ms){ 445 | this.runnable().slow(ms); 446 | return this; 447 | }; 448 | 449 | /** 450 | * Inspect the context void of `._runnable`. 451 | * 452 | * @return {String} 453 | * @api private 454 | */ 455 | 456 | Context.prototype.inspect = function(){ 457 | return JSON.stringify(this, function(key, val){ 458 | if ('_runnable' == key) return; 459 | if ('test' == key) return; 460 | return val; 461 | }, 2); 462 | }; 463 | 464 | }); // module: context.js 465 | 466 | require.register("hook.js", function(module, exports, require){ 467 | 468 | /** 469 | * Module dependencies. 470 | */ 471 | 472 | var Runnable = require('./runnable'); 473 | 474 | /** 475 | * Expose `Hook`. 476 | */ 477 | 478 | module.exports = Hook; 479 | 480 | /** 481 | * Initialize a new `Hook` with the given `title` and callback `fn`. 482 | * 483 | * @param {String} title 484 | * @param {Function} fn 485 | * @api private 486 | */ 487 | 488 | function Hook(title, fn) { 489 | Runnable.call(this, title, fn); 490 | this.type = 'hook'; 491 | } 492 | 493 | /** 494 | * Inherit from `Runnable.prototype`. 495 | */ 496 | 497 | Hook.prototype = new Runnable; 498 | Hook.prototype.constructor = Hook; 499 | 500 | 501 | /** 502 | * Get or set the test `err`. 503 | * 504 | * @param {Error} err 505 | * @return {Error} 506 | * @api public 507 | */ 508 | 509 | Hook.prototype.error = function(err){ 510 | if (0 == arguments.length) { 511 | var err = this._error; 512 | this._error = null; 513 | return err; 514 | } 515 | 516 | this._error = err; 517 | }; 518 | 519 | 520 | }); // module: hook.js 521 | 522 | require.register("interfaces/bdd.js", function(module, exports, require){ 523 | 524 | /** 525 | * Module dependencies. 526 | */ 527 | 528 | var Suite = require('../suite') 529 | , Test = require('../test'); 530 | 531 | /** 532 | * BDD-style interface: 533 | * 534 | * describe('Array', function(){ 535 | * describe('#indexOf()', function(){ 536 | * it('should return -1 when not present', function(){ 537 | * 538 | * }); 539 | * 540 | * it('should return the index when present', function(){ 541 | * 542 | * }); 543 | * }); 544 | * }); 545 | * 546 | */ 547 | 548 | module.exports = function(suite){ 549 | var suites = [suite]; 550 | 551 | suite.on('pre-require', function(context, file, mocha){ 552 | 553 | /** 554 | * Execute before running tests. 555 | */ 556 | 557 | context.before = function(fn){ 558 | suites[0].beforeAll(fn); 559 | }; 560 | 561 | /** 562 | * Execute after running tests. 563 | */ 564 | 565 | context.after = function(fn){ 566 | suites[0].afterAll(fn); 567 | }; 568 | 569 | /** 570 | * Execute before each test case. 571 | */ 572 | 573 | context.beforeEach = function(fn){ 574 | suites[0].beforeEach(fn); 575 | }; 576 | 577 | /** 578 | * Execute after each test case. 579 | */ 580 | 581 | context.afterEach = function(fn){ 582 | suites[0].afterEach(fn); 583 | }; 584 | 585 | /** 586 | * Describe a "suite" with the given `title` 587 | * and callback `fn` containing nested suites 588 | * and/or tests. 589 | */ 590 | 591 | context.describe = context.context = function(title, fn){ 592 | var suite = Suite.create(suites[0], title); 593 | suites.unshift(suite); 594 | fn.call(suite); 595 | suites.shift(); 596 | return suite; 597 | }; 598 | 599 | /** 600 | * Pending describe. 601 | */ 602 | 603 | context.xdescribe = 604 | context.xcontext = 605 | context.describe.skip = function(title, fn){ 606 | var suite = Suite.create(suites[0], title); 607 | suite.pending = true; 608 | suites.unshift(suite); 609 | fn.call(suite); 610 | suites.shift(); 611 | }; 612 | 613 | /** 614 | * Exclusive suite. 615 | */ 616 | 617 | context.describe.only = function(title, fn){ 618 | var suite = context.describe(title, fn); 619 | mocha.grep(suite.fullTitle()); 620 | }; 621 | 622 | /** 623 | * Describe a specification or test-case 624 | * with the given `title` and callback `fn` 625 | * acting as a thunk. 626 | */ 627 | 628 | context.it = context.specify = function(title, fn){ 629 | var suite = suites[0]; 630 | if (suite.pending) var fn = null; 631 | var test = new Test(title, fn); 632 | suite.addTest(test); 633 | return test; 634 | }; 635 | 636 | /** 637 | * Exclusive test-case. 638 | */ 639 | 640 | context.it.only = function(title, fn){ 641 | var test = context.it(title, fn); 642 | mocha.grep(test.fullTitle()); 643 | }; 644 | 645 | /** 646 | * Pending test case. 647 | */ 648 | 649 | context.xit = 650 | context.xspecify = 651 | context.it.skip = function(title){ 652 | context.it(title); 653 | }; 654 | }); 655 | }; 656 | 657 | }); // module: interfaces/bdd.js 658 | 659 | require.register("interfaces/exports.js", function(module, exports, require){ 660 | 661 | /** 662 | * Module dependencies. 663 | */ 664 | 665 | var Suite = require('../suite') 666 | , Test = require('../test'); 667 | 668 | /** 669 | * TDD-style interface: 670 | * 671 | * exports.Array = { 672 | * '#indexOf()': { 673 | * 'should return -1 when the value is not present': function(){ 674 | * 675 | * }, 676 | * 677 | * 'should return the correct index when the value is present': function(){ 678 | * 679 | * } 680 | * } 681 | * }; 682 | * 683 | */ 684 | 685 | module.exports = function(suite){ 686 | var suites = [suite]; 687 | 688 | suite.on('require', visit); 689 | 690 | function visit(obj) { 691 | var suite; 692 | for (var key in obj) { 693 | if ('function' == typeof obj[key]) { 694 | var fn = obj[key]; 695 | switch (key) { 696 | case 'before': 697 | suites[0].beforeAll(fn); 698 | break; 699 | case 'after': 700 | suites[0].afterAll(fn); 701 | break; 702 | case 'beforeEach': 703 | suites[0].beforeEach(fn); 704 | break; 705 | case 'afterEach': 706 | suites[0].afterEach(fn); 707 | break; 708 | default: 709 | suites[0].addTest(new Test(key, fn)); 710 | } 711 | } else { 712 | var suite = Suite.create(suites[0], key); 713 | suites.unshift(suite); 714 | visit(obj[key]); 715 | suites.shift(); 716 | } 717 | } 718 | } 719 | }; 720 | }); // module: interfaces/exports.js 721 | 722 | require.register("interfaces/index.js", function(module, exports, require){ 723 | 724 | exports.bdd = require('./bdd'); 725 | exports.tdd = require('./tdd'); 726 | exports.qunit = require('./qunit'); 727 | exports.exports = require('./exports'); 728 | 729 | }); // module: interfaces/index.js 730 | 731 | require.register("interfaces/qunit.js", function(module, exports, require){ 732 | 733 | /** 734 | * Module dependencies. 735 | */ 736 | 737 | var Suite = require('../suite') 738 | , Test = require('../test'); 739 | 740 | /** 741 | * QUnit-style interface: 742 | * 743 | * suite('Array'); 744 | * 745 | * test('#length', function(){ 746 | * var arr = [1,2,3]; 747 | * ok(arr.length == 3); 748 | * }); 749 | * 750 | * test('#indexOf()', function(){ 751 | * var arr = [1,2,3]; 752 | * ok(arr.indexOf(1) == 0); 753 | * ok(arr.indexOf(2) == 1); 754 | * ok(arr.indexOf(3) == 2); 755 | * }); 756 | * 757 | * suite('String'); 758 | * 759 | * test('#length', function(){ 760 | * ok('foo'.length == 3); 761 | * }); 762 | * 763 | */ 764 | 765 | module.exports = function(suite){ 766 | var suites = [suite]; 767 | 768 | suite.on('pre-require', function(context){ 769 | 770 | /** 771 | * Execute before running tests. 772 | */ 773 | 774 | context.before = function(fn){ 775 | suites[0].beforeAll(fn); 776 | }; 777 | 778 | /** 779 | * Execute after running tests. 780 | */ 781 | 782 | context.after = function(fn){ 783 | suites[0].afterAll(fn); 784 | }; 785 | 786 | /** 787 | * Execute before each test case. 788 | */ 789 | 790 | context.beforeEach = function(fn){ 791 | suites[0].beforeEach(fn); 792 | }; 793 | 794 | /** 795 | * Execute after each test case. 796 | */ 797 | 798 | context.afterEach = function(fn){ 799 | suites[0].afterEach(fn); 800 | }; 801 | 802 | /** 803 | * Describe a "suite" with the given `title`. 804 | */ 805 | 806 | context.suite = function(title){ 807 | if (suites.length > 1) suites.shift(); 808 | var suite = Suite.create(suites[0], title); 809 | suites.unshift(suite); 810 | }; 811 | 812 | /** 813 | * Describe a specification or test-case 814 | * with the given `title` and callback `fn` 815 | * acting as a thunk. 816 | */ 817 | 818 | context.test = function(title, fn){ 819 | suites[0].addTest(new Test(title, fn)); 820 | }; 821 | }); 822 | }; 823 | 824 | }); // module: interfaces/qunit.js 825 | 826 | require.register("interfaces/tdd.js", function(module, exports, require){ 827 | 828 | /** 829 | * Module dependencies. 830 | */ 831 | 832 | var Suite = require('../suite') 833 | , Test = require('../test'); 834 | 835 | /** 836 | * TDD-style interface: 837 | * 838 | * suite('Array', function(){ 839 | * suite('#indexOf()', function(){ 840 | * suiteSetup(function(){ 841 | * 842 | * }); 843 | * 844 | * test('should return -1 when not present', function(){ 845 | * 846 | * }); 847 | * 848 | * test('should return the index when present', function(){ 849 | * 850 | * }); 851 | * 852 | * suiteTeardown(function(){ 853 | * 854 | * }); 855 | * }); 856 | * }); 857 | * 858 | */ 859 | 860 | module.exports = function(suite){ 861 | var suites = [suite]; 862 | 863 | suite.on('pre-require', function(context, file, mocha){ 864 | 865 | /** 866 | * Execute before each test case. 867 | */ 868 | 869 | context.setup = function(fn){ 870 | suites[0].beforeEach(fn); 871 | }; 872 | 873 | /** 874 | * Execute after each test case. 875 | */ 876 | 877 | context.teardown = function(fn){ 878 | suites[0].afterEach(fn); 879 | }; 880 | 881 | /** 882 | * Execute before the suite. 883 | */ 884 | 885 | context.suiteSetup = function(fn){ 886 | suites[0].beforeAll(fn); 887 | }; 888 | 889 | /** 890 | * Execute after the suite. 891 | */ 892 | 893 | context.suiteTeardown = function(fn){ 894 | suites[0].afterAll(fn); 895 | }; 896 | 897 | /** 898 | * Describe a "suite" with the given `title` 899 | * and callback `fn` containing nested suites 900 | * and/or tests. 901 | */ 902 | 903 | context.suite = function(title, fn){ 904 | var suite = Suite.create(suites[0], title); 905 | suites.unshift(suite); 906 | fn.call(suite); 907 | suites.shift(); 908 | return suite; 909 | }; 910 | 911 | /** 912 | * Exclusive test-case. 913 | */ 914 | 915 | context.suite.only = function(title, fn){ 916 | var suite = context.suite(title, fn); 917 | mocha.grep(suite.fullTitle()); 918 | }; 919 | 920 | /** 921 | * Describe a specification or test-case 922 | * with the given `title` and callback `fn` 923 | * acting as a thunk. 924 | */ 925 | 926 | context.test = function(title, fn){ 927 | var test = new Test(title, fn); 928 | suites[0].addTest(test); 929 | return test; 930 | }; 931 | 932 | /** 933 | * Exclusive test-case. 934 | */ 935 | 936 | context.test.only = function(title, fn){ 937 | var test = context.test(title, fn); 938 | mocha.grep(test.fullTitle()); 939 | }; 940 | 941 | /** 942 | * Pending test case. 943 | */ 944 | 945 | context.test.skip = function(title){ 946 | context.test(title); 947 | }; 948 | }); 949 | }; 950 | 951 | }); // module: interfaces/tdd.js 952 | 953 | require.register("mocha.js", function(module, exports, require){ 954 | /*! 955 | * mocha 956 | * Copyright(c) 2011 TJ Holowaychuk 957 | * MIT Licensed 958 | */ 959 | 960 | /** 961 | * Module dependencies. 962 | */ 963 | 964 | var path = require('browser/path') 965 | , utils = require('./utils'); 966 | 967 | /** 968 | * Expose `Mocha`. 969 | */ 970 | 971 | exports = module.exports = Mocha; 972 | 973 | /** 974 | * Expose internals. 975 | */ 976 | 977 | exports.utils = utils; 978 | exports.interfaces = require('./interfaces'); 979 | exports.reporters = require('./reporters'); 980 | exports.Runnable = require('./runnable'); 981 | exports.Context = require('./context'); 982 | exports.Runner = require('./runner'); 983 | exports.Suite = require('./suite'); 984 | exports.Hook = require('./hook'); 985 | exports.Test = require('./test'); 986 | 987 | /** 988 | * Return image `name` path. 989 | * 990 | * @param {String} name 991 | * @return {String} 992 | * @api private 993 | */ 994 | 995 | function image(name) { 996 | return __dirname + '/../images/' + name + '.png'; 997 | } 998 | 999 | /** 1000 | * Setup mocha with `options`. 1001 | * 1002 | * Options: 1003 | * 1004 | * - `ui` name "bdd", "tdd", "exports" etc 1005 | * - `reporter` reporter instance, defaults to `mocha.reporters.Dot` 1006 | * - `globals` array of accepted globals 1007 | * - `timeout` timeout in milliseconds 1008 | * - `slow` milliseconds to wait before considering a test slow 1009 | * - `ignoreLeaks` ignore global leaks 1010 | * - `grep` string or regexp to filter tests with 1011 | * 1012 | * @param {Object} options 1013 | * @api public 1014 | */ 1015 | 1016 | function Mocha(options) { 1017 | options = options || {}; 1018 | this.files = []; 1019 | this.options = options; 1020 | this.grep(options.grep); 1021 | this.suite = new exports.Suite('', new exports.Context); 1022 | this.ui(options.ui); 1023 | this.reporter(options.reporter); 1024 | if (options.timeout) this.timeout(options.timeout); 1025 | if (options.slow) this.slow(options.slow); 1026 | } 1027 | 1028 | /** 1029 | * Add test `file`. 1030 | * 1031 | * @param {String} file 1032 | * @api public 1033 | */ 1034 | 1035 | Mocha.prototype.addFile = function(file){ 1036 | this.files.push(file); 1037 | return this; 1038 | }; 1039 | 1040 | /** 1041 | * Set reporter to `reporter`, defaults to "dot". 1042 | * 1043 | * @param {String|Function} reporter name of a reporter or a reporter constructor 1044 | * @api public 1045 | */ 1046 | 1047 | Mocha.prototype.reporter = function(reporter){ 1048 | if ('function' == typeof reporter) { 1049 | this._reporter = reporter; 1050 | } else { 1051 | reporter = reporter || 'dot'; 1052 | try { 1053 | this._reporter = require('./reporters/' + reporter); 1054 | } catch (err) { 1055 | this._reporter = require(reporter); 1056 | } 1057 | if (!this._reporter) throw new Error('invalid reporter "' + reporter + '"'); 1058 | } 1059 | return this; 1060 | }; 1061 | 1062 | /** 1063 | * Set test UI `name`, defaults to "bdd". 1064 | * 1065 | * @param {String} bdd 1066 | * @api public 1067 | */ 1068 | 1069 | Mocha.prototype.ui = function(name){ 1070 | name = name || 'bdd'; 1071 | this._ui = exports.interfaces[name]; 1072 | if (!this._ui) throw new Error('invalid interface "' + name + '"'); 1073 | this._ui = this._ui(this.suite); 1074 | return this; 1075 | }; 1076 | 1077 | /** 1078 | * Load registered files. 1079 | * 1080 | * @api private 1081 | */ 1082 | 1083 | Mocha.prototype.loadFiles = function(fn){ 1084 | var self = this; 1085 | var suite = this.suite; 1086 | var pending = this.files.length; 1087 | this.files.forEach(function(file){ 1088 | file = path.resolve(file); 1089 | suite.emit('pre-require', global, file, self); 1090 | suite.emit('require', require(file), file, self); 1091 | suite.emit('post-require', global, file, self); 1092 | --pending || (fn && fn()); 1093 | }); 1094 | }; 1095 | 1096 | /** 1097 | * Enable growl support. 1098 | * 1099 | * @api private 1100 | */ 1101 | 1102 | Mocha.prototype._growl = function(runner, reporter) { 1103 | var notify = require('growl'); 1104 | 1105 | runner.on('end', function(){ 1106 | var stats = reporter.stats; 1107 | if (stats.failures) { 1108 | var msg = stats.failures + ' of ' + runner.total + ' tests failed'; 1109 | notify(msg, { name: 'mocha', title: 'Failed', image: image('error') }); 1110 | } else { 1111 | notify(stats.passes + ' tests passed in ' + stats.duration + 'ms', { 1112 | name: 'mocha' 1113 | , title: 'Passed' 1114 | , image: image('ok') 1115 | }); 1116 | } 1117 | }); 1118 | }; 1119 | 1120 | /** 1121 | * Add regexp to grep, if `re` is a string it is escaped. 1122 | * 1123 | * @param {RegExp|String} re 1124 | * @return {Mocha} 1125 | * @api public 1126 | */ 1127 | 1128 | Mocha.prototype.grep = function(re){ 1129 | this.options.grep = 'string' == typeof re 1130 | ? new RegExp(utils.escapeRegexp(re)) 1131 | : re; 1132 | return this; 1133 | }; 1134 | 1135 | /** 1136 | * Invert `.grep()` matches. 1137 | * 1138 | * @return {Mocha} 1139 | * @api public 1140 | */ 1141 | 1142 | Mocha.prototype.invert = function(){ 1143 | this.options.invert = true; 1144 | return this; 1145 | }; 1146 | 1147 | /** 1148 | * Ignore global leaks. 1149 | * 1150 | * @return {Mocha} 1151 | * @api public 1152 | */ 1153 | 1154 | Mocha.prototype.ignoreLeaks = function(){ 1155 | this.options.ignoreLeaks = true; 1156 | return this; 1157 | }; 1158 | 1159 | /** 1160 | * Enable global leak checking. 1161 | * 1162 | * @return {Mocha} 1163 | * @api public 1164 | */ 1165 | 1166 | Mocha.prototype.checkLeaks = function(){ 1167 | this.options.ignoreLeaks = false; 1168 | return this; 1169 | }; 1170 | 1171 | /** 1172 | * Enable growl support. 1173 | * 1174 | * @return {Mocha} 1175 | * @api public 1176 | */ 1177 | 1178 | Mocha.prototype.growl = function(){ 1179 | this.options.growl = true; 1180 | return this; 1181 | }; 1182 | 1183 | /** 1184 | * Ignore `globals` array or string. 1185 | * 1186 | * @param {Array|String} globals 1187 | * @return {Mocha} 1188 | * @api public 1189 | */ 1190 | 1191 | Mocha.prototype.globals = function(globals){ 1192 | this.options.globals = (this.options.globals || []).concat(globals); 1193 | return this; 1194 | }; 1195 | 1196 | /** 1197 | * Set the timeout in milliseconds. 1198 | * 1199 | * @param {Number} timeout 1200 | * @return {Mocha} 1201 | * @api public 1202 | */ 1203 | 1204 | Mocha.prototype.timeout = function(timeout){ 1205 | this.suite.timeout(timeout); 1206 | return this; 1207 | }; 1208 | 1209 | /** 1210 | * Set slowness threshold in milliseconds. 1211 | * 1212 | * @param {Number} slow 1213 | * @return {Mocha} 1214 | * @api public 1215 | */ 1216 | 1217 | Mocha.prototype.slow = function(slow){ 1218 | this.suite.slow(slow); 1219 | return this; 1220 | }; 1221 | 1222 | /** 1223 | * Makes all tests async (accepting a callback) 1224 | * 1225 | * @return {Mocha} 1226 | * @api public 1227 | */ 1228 | 1229 | Mocha.prototype.asyncOnly = function(){ 1230 | this.options.asyncOnly = true; 1231 | return this; 1232 | }; 1233 | 1234 | /** 1235 | * Run tests and invoke `fn()` when complete. 1236 | * 1237 | * @param {Function} fn 1238 | * @return {Runner} 1239 | * @api public 1240 | */ 1241 | 1242 | Mocha.prototype.run = function(fn){ 1243 | if (this.files.length) this.loadFiles(); 1244 | var suite = this.suite; 1245 | var options = this.options; 1246 | var runner = new exports.Runner(suite); 1247 | var reporter = new this._reporter(runner); 1248 | runner.ignoreLeaks = options.ignoreLeaks; 1249 | runner.asyncOnly = options.asyncOnly; 1250 | if (options.grep) runner.grep(options.grep, options.invert); 1251 | if (options.globals) runner.globals(options.globals); 1252 | if (options.growl) this._growl(runner, reporter); 1253 | return runner.run(fn); 1254 | }; 1255 | 1256 | }); // module: mocha.js 1257 | 1258 | require.register("ms.js", function(module, exports, require){ 1259 | 1260 | /** 1261 | * Helpers. 1262 | */ 1263 | 1264 | var s = 1000; 1265 | var m = s * 60; 1266 | var h = m * 60; 1267 | var d = h * 24; 1268 | 1269 | /** 1270 | * Parse or format the given `val`. 1271 | * 1272 | * @param {String|Number} val 1273 | * @return {String|Number} 1274 | * @api public 1275 | */ 1276 | 1277 | module.exports = function(val){ 1278 | if ('string' == typeof val) return parse(val); 1279 | return format(val); 1280 | } 1281 | 1282 | /** 1283 | * Parse the given `str` and return milliseconds. 1284 | * 1285 | * @param {String} str 1286 | * @return {Number} 1287 | * @api private 1288 | */ 1289 | 1290 | function parse(str) { 1291 | var m = /^((?:\d+)?\.?\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i.exec(str); 1292 | if (!m) return; 1293 | var n = parseFloat(m[1]); 1294 | var type = (m[2] || 'ms').toLowerCase(); 1295 | switch (type) { 1296 | case 'years': 1297 | case 'year': 1298 | case 'y': 1299 | return n * 31557600000; 1300 | case 'days': 1301 | case 'day': 1302 | case 'd': 1303 | return n * 86400000; 1304 | case 'hours': 1305 | case 'hour': 1306 | case 'h': 1307 | return n * 3600000; 1308 | case 'minutes': 1309 | case 'minute': 1310 | case 'm': 1311 | return n * 60000; 1312 | case 'seconds': 1313 | case 'second': 1314 | case 's': 1315 | return n * 1000; 1316 | case 'ms': 1317 | return n; 1318 | } 1319 | } 1320 | 1321 | /** 1322 | * Format the given `ms`. 1323 | * 1324 | * @param {Number} ms 1325 | * @return {String} 1326 | * @api public 1327 | */ 1328 | 1329 | function format(ms) { 1330 | if (ms == d) return Math.round(ms / d) + ' day'; 1331 | if (ms > d) return Math.round(ms / d) + ' days'; 1332 | if (ms == h) return Math.round(ms / h) + ' hour'; 1333 | if (ms > h) return Math.round(ms / h) + ' hours'; 1334 | if (ms == m) return Math.round(ms / m) + ' minute'; 1335 | if (ms > m) return Math.round(ms / m) + ' minutes'; 1336 | if (ms == s) return Math.round(ms / s) + ' second'; 1337 | if (ms > s) return Math.round(ms / s) + ' seconds'; 1338 | return ms + ' ms'; 1339 | } 1340 | }); // module: ms.js 1341 | 1342 | require.register("reporters/base.js", function(module, exports, require){ 1343 | 1344 | /** 1345 | * Module dependencies. 1346 | */ 1347 | 1348 | var tty = require('browser/tty') 1349 | , diff = require('browser/diff') 1350 | , ms = require('../ms'); 1351 | 1352 | /** 1353 | * Save timer references to avoid Sinon interfering (see GH-237). 1354 | */ 1355 | 1356 | var Date = global.Date 1357 | , setTimeout = global.setTimeout 1358 | , setInterval = global.setInterval 1359 | , clearTimeout = global.clearTimeout 1360 | , clearInterval = global.clearInterval; 1361 | 1362 | /** 1363 | * Check if both stdio streams are associated with a tty. 1364 | */ 1365 | 1366 | var isatty = tty.isatty(1) && tty.isatty(2); 1367 | 1368 | /** 1369 | * Expose `Base`. 1370 | */ 1371 | 1372 | exports = module.exports = Base; 1373 | 1374 | /** 1375 | * Enable coloring by default. 1376 | */ 1377 | 1378 | exports.useColors = isatty; 1379 | 1380 | /** 1381 | * Default color map. 1382 | */ 1383 | 1384 | exports.colors = { 1385 | 'pass': 90 1386 | , 'fail': 31 1387 | , 'bright pass': 92 1388 | , 'bright fail': 91 1389 | , 'bright yellow': 93 1390 | , 'pending': 36 1391 | , 'suite': 0 1392 | , 'error title': 0 1393 | , 'error message': 31 1394 | , 'error stack': 90 1395 | , 'checkmark': 32 1396 | , 'fast': 90 1397 | , 'medium': 33 1398 | , 'slow': 31 1399 | , 'green': 32 1400 | , 'light': 90 1401 | , 'diff gutter': 90 1402 | , 'diff added': 42 1403 | , 'diff removed': 41 1404 | }; 1405 | 1406 | /** 1407 | * Default symbol map. 1408 | */ 1409 | 1410 | exports.symbols = { 1411 | ok: '✓', 1412 | err: '✖', 1413 | dot: '․' 1414 | }; 1415 | 1416 | // With node.js on Windows: use symbols available in terminal default fonts 1417 | if ('win32' == process.platform) { 1418 | exports.symbols.ok = '\u221A'; 1419 | exports.symbols.err = '\u00D7'; 1420 | exports.symbols.dot = '.'; 1421 | } 1422 | 1423 | /** 1424 | * Color `str` with the given `type`, 1425 | * allowing colors to be disabled, 1426 | * as well as user-defined color 1427 | * schemes. 1428 | * 1429 | * @param {String} type 1430 | * @param {String} str 1431 | * @return {String} 1432 | * @api private 1433 | */ 1434 | 1435 | var color = exports.color = function(type, str) { 1436 | if (!exports.useColors) return str; 1437 | return '\u001b[' + exports.colors[type] + 'm' + str + '\u001b[0m'; 1438 | }; 1439 | 1440 | /** 1441 | * Expose term window size, with some 1442 | * defaults for when stderr is not a tty. 1443 | */ 1444 | 1445 | exports.window = { 1446 | width: isatty 1447 | ? process.stdout.getWindowSize 1448 | ? process.stdout.getWindowSize(1)[0] 1449 | : tty.getWindowSize()[1] 1450 | : 75 1451 | }; 1452 | 1453 | /** 1454 | * Expose some basic cursor interactions 1455 | * that are common among reporters. 1456 | */ 1457 | 1458 | exports.cursor = { 1459 | hide: function(){ 1460 | process.stdout.write('\u001b[?25l'); 1461 | }, 1462 | 1463 | show: function(){ 1464 | process.stdout.write('\u001b[?25h'); 1465 | }, 1466 | 1467 | deleteLine: function(){ 1468 | process.stdout.write('\u001b[2K'); 1469 | }, 1470 | 1471 | beginningOfLine: function(){ 1472 | process.stdout.write('\u001b[0G'); 1473 | }, 1474 | 1475 | CR: function(){ 1476 | exports.cursor.deleteLine(); 1477 | exports.cursor.beginningOfLine(); 1478 | } 1479 | }; 1480 | 1481 | /** 1482 | * Outut the given `failures` as a list. 1483 | * 1484 | * @param {Array} failures 1485 | * @api public 1486 | */ 1487 | 1488 | exports.list = function(failures){ 1489 | console.error(); 1490 | failures.forEach(function(test, i){ 1491 | // format 1492 | var fmt = color('error title', ' %s) %s:\n') 1493 | + color('error message', ' %s') 1494 | + color('error stack', '\n%s\n'); 1495 | 1496 | // msg 1497 | var err = test.err 1498 | , message = err.message || '' 1499 | , stack = err.stack || message 1500 | , index = stack.indexOf(message) + message.length 1501 | , msg = stack.slice(0, index) 1502 | , actual = err.actual 1503 | , expected = err.expected 1504 | , escape = true; 1505 | 1506 | // explicitly show diff 1507 | if (err.showDiff) { 1508 | escape = false; 1509 | err.actual = actual = JSON.stringify(actual, null, 2); 1510 | err.expected = expected = JSON.stringify(expected, null, 2); 1511 | } 1512 | 1513 | // actual / expected diff 1514 | if ('string' == typeof actual && 'string' == typeof expected) { 1515 | var len = Math.max(actual.length, expected.length); 1516 | 1517 | if (len < 20) msg = errorDiff(err, 'Chars', escape); 1518 | else msg = errorDiff(err, 'Words', escape); 1519 | 1520 | // linenos 1521 | var lines = msg.split('\n'); 1522 | if (lines.length > 4) { 1523 | var width = String(lines.length).length; 1524 | msg = lines.map(function(str, i){ 1525 | return pad(++i, width) + ' |' + ' ' + str; 1526 | }).join('\n'); 1527 | } 1528 | 1529 | // legend 1530 | msg = '\n' 1531 | + color('diff removed', 'actual') 1532 | + ' ' 1533 | + color('diff added', 'expected') 1534 | + '\n\n' 1535 | + msg 1536 | + '\n'; 1537 | 1538 | // indent 1539 | msg = msg.replace(/^/gm, ' '); 1540 | 1541 | fmt = color('error title', ' %s) %s:\n%s') 1542 | + color('error stack', '\n%s\n'); 1543 | } 1544 | 1545 | // indent stack trace without msg 1546 | stack = stack.slice(index ? index + 1 : index) 1547 | .replace(/^/gm, ' '); 1548 | 1549 | console.error(fmt, (i + 1), test.fullTitle(), msg, stack); 1550 | }); 1551 | }; 1552 | 1553 | /** 1554 | * Initialize a new `Base` reporter. 1555 | * 1556 | * All other reporters generally 1557 | * inherit from this reporter, providing 1558 | * stats such as test duration, number 1559 | * of tests passed / failed etc. 1560 | * 1561 | * @param {Runner} runner 1562 | * @api public 1563 | */ 1564 | 1565 | function Base(runner) { 1566 | var self = this 1567 | , stats = this.stats = { suites: 0, tests: 0, passes: 0, pending: 0, failures: 0 } 1568 | , failures = this.failures = []; 1569 | 1570 | if (!runner) return; 1571 | this.runner = runner; 1572 | 1573 | runner.stats = stats; 1574 | 1575 | runner.on('start', function(){ 1576 | stats.start = new Date; 1577 | }); 1578 | 1579 | runner.on('suite', function(suite){ 1580 | stats.suites = stats.suites || 0; 1581 | suite.root || stats.suites++; 1582 | }); 1583 | 1584 | runner.on('test end', function(test){ 1585 | stats.tests = stats.tests || 0; 1586 | stats.tests++; 1587 | }); 1588 | 1589 | runner.on('pass', function(test){ 1590 | stats.passes = stats.passes || 0; 1591 | 1592 | var medium = test.slow() / 2; 1593 | test.speed = test.duration > test.slow() 1594 | ? 'slow' 1595 | : test.duration > medium 1596 | ? 'medium' 1597 | : 'fast'; 1598 | 1599 | stats.passes++; 1600 | }); 1601 | 1602 | runner.on('fail', function(test, err){ 1603 | stats.failures = stats.failures || 0; 1604 | stats.failures++; 1605 | test.err = err; 1606 | failures.push(test); 1607 | }); 1608 | 1609 | runner.on('end', function(){ 1610 | stats.end = new Date; 1611 | stats.duration = new Date - stats.start; 1612 | }); 1613 | 1614 | runner.on('pending', function(){ 1615 | stats.pending++; 1616 | }); 1617 | } 1618 | 1619 | /** 1620 | * Output common epilogue used by many of 1621 | * the bundled reporters. 1622 | * 1623 | * @api public 1624 | */ 1625 | 1626 | Base.prototype.epilogue = function(){ 1627 | var stats = this.stats 1628 | , fmt 1629 | , tests; 1630 | 1631 | console.log(); 1632 | 1633 | function pluralize(n) { 1634 | return 1 == n ? 'test' : 'tests'; 1635 | } 1636 | 1637 | // failure 1638 | if (stats.failures) { 1639 | fmt = color('bright fail', ' ' + exports.symbols.err) 1640 | + color('fail', ' %d of %d %s failed') 1641 | + color('light', ':') 1642 | 1643 | console.error(fmt, 1644 | stats.failures, 1645 | this.runner.total, 1646 | pluralize(this.runner.total)); 1647 | 1648 | Base.list(this.failures); 1649 | console.error(); 1650 | return; 1651 | } 1652 | 1653 | // pass 1654 | fmt = color('bright pass', ' ') 1655 | + color('green', ' %d %s complete') 1656 | + color('light', ' (%s)'); 1657 | 1658 | console.log(fmt, 1659 | stats.tests || 0, 1660 | pluralize(stats.tests), 1661 | ms(stats.duration)); 1662 | 1663 | // pending 1664 | if (stats.pending) { 1665 | fmt = color('pending', ' ') 1666 | + color('pending', ' %d %s pending'); 1667 | 1668 | console.log(fmt, stats.pending, pluralize(stats.pending)); 1669 | } 1670 | 1671 | console.log(); 1672 | }; 1673 | 1674 | /** 1675 | * Pad the given `str` to `len`. 1676 | * 1677 | * @param {String} str 1678 | * @param {String} len 1679 | * @return {String} 1680 | * @api private 1681 | */ 1682 | 1683 | function pad(str, len) { 1684 | str = String(str); 1685 | return Array(len - str.length + 1).join(' ') + str; 1686 | } 1687 | 1688 | /** 1689 | * Return a character diff for `err`. 1690 | * 1691 | * @param {Error} err 1692 | * @return {String} 1693 | * @api private 1694 | */ 1695 | 1696 | function errorDiff(err, type, escape) { 1697 | return diff['diff' + type](err.actual, err.expected).map(function(str){ 1698 | if (escape) { 1699 | str.value = str.value 1700 | .replace(/\t/g, '') 1701 | .replace(/\r/g, '') 1702 | .replace(/\n/g, '\n'); 1703 | } 1704 | if (str.added) return colorLines('diff added', str.value); 1705 | if (str.removed) return colorLines('diff removed', str.value); 1706 | return str.value; 1707 | }).join(''); 1708 | } 1709 | 1710 | /** 1711 | * Color lines for `str`, using the color `name`. 1712 | * 1713 | * @param {String} name 1714 | * @param {String} str 1715 | * @return {String} 1716 | * @api private 1717 | */ 1718 | 1719 | function colorLines(name, str) { 1720 | return str.split('\n').map(function(str){ 1721 | return color(name, str); 1722 | }).join('\n'); 1723 | } 1724 | 1725 | }); // module: reporters/base.js 1726 | 1727 | require.register("reporters/doc.js", function(module, exports, require){ 1728 | 1729 | /** 1730 | * Module dependencies. 1731 | */ 1732 | 1733 | var Base = require('./base') 1734 | , utils = require('../utils'); 1735 | 1736 | /** 1737 | * Expose `Doc`. 1738 | */ 1739 | 1740 | exports = module.exports = Doc; 1741 | 1742 | /** 1743 | * Initialize a new `Doc` reporter. 1744 | * 1745 | * @param {Runner} runner 1746 | * @api public 1747 | */ 1748 | 1749 | function Doc(runner) { 1750 | Base.call(this, runner); 1751 | 1752 | var self = this 1753 | , stats = this.stats 1754 | , total = runner.total 1755 | , indents = 2; 1756 | 1757 | function indent() { 1758 | return Array(indents).join(' '); 1759 | } 1760 | 1761 | runner.on('suite', function(suite){ 1762 | if (suite.root) return; 1763 | ++indents; 1764 | console.log('%s', indent()); 1765 | ++indents; 1766 | console.log('%s%s', indent(), utils.escape(suite.title)); 1767 | console.log('%s', indent()); 1768 | }); 1769 | 1770 | runner.on('suite end', function(suite){ 1771 | if (suite.root) return; 1772 | console.log('%s', indent()); 1773 | --indents; 1774 | console.log('%s', indent()); 1775 | --indents; 1776 | }); 1777 | 1778 | runner.on('pass', function(test){ 1779 | console.log('%s %s', indent(), utils.escape(test.title)); 1780 | var code = utils.escape(utils.clean(test.fn.toString())); 1781 | console.log('%s %s', indent(), code); 1782 | }); 1783 | } 1784 | 1785 | }); // module: reporters/doc.js 1786 | 1787 | require.register("reporters/dot.js", function(module, exports, require){ 1788 | 1789 | /** 1790 | * Module dependencies. 1791 | */ 1792 | 1793 | var Base = require('./base') 1794 | , color = Base.color; 1795 | 1796 | /** 1797 | * Expose `Dot`. 1798 | */ 1799 | 1800 | exports = module.exports = Dot; 1801 | 1802 | /** 1803 | * Initialize a new `Dot` matrix test reporter. 1804 | * 1805 | * @param {Runner} runner 1806 | * @api public 1807 | */ 1808 | 1809 | function Dot(runner) { 1810 | Base.call(this, runner); 1811 | 1812 | var self = this 1813 | , stats = this.stats 1814 | , width = Base.window.width * .75 | 0 1815 | , n = 0; 1816 | 1817 | runner.on('start', function(){ 1818 | process.stdout.write('\n '); 1819 | }); 1820 | 1821 | runner.on('pending', function(test){ 1822 | process.stdout.write(color('pending', Base.symbols.dot)); 1823 | }); 1824 | 1825 | runner.on('pass', function(test){ 1826 | if (++n % width == 0) process.stdout.write('\n '); 1827 | if ('slow' == test.speed) { 1828 | process.stdout.write(color('bright yellow', Base.symbols.dot)); 1829 | } else { 1830 | process.stdout.write(color(test.speed, Base.symbols.dot)); 1831 | } 1832 | }); 1833 | 1834 | runner.on('fail', function(test, err){ 1835 | if (++n % width == 0) process.stdout.write('\n '); 1836 | process.stdout.write(color('fail', Base.symbols.dot)); 1837 | }); 1838 | 1839 | runner.on('end', function(){ 1840 | console.log(); 1841 | self.epilogue(); 1842 | }); 1843 | } 1844 | 1845 | /** 1846 | * Inherit from `Base.prototype`. 1847 | */ 1848 | 1849 | Dot.prototype = new Base; 1850 | Dot.prototype.constructor = Dot; 1851 | 1852 | }); // module: reporters/dot.js 1853 | 1854 | require.register("reporters/html-cov.js", function(module, exports, require){ 1855 | 1856 | /** 1857 | * Module dependencies. 1858 | */ 1859 | 1860 | var JSONCov = require('./json-cov') 1861 | , fs = require('browser/fs'); 1862 | 1863 | /** 1864 | * Expose `HTMLCov`. 1865 | */ 1866 | 1867 | exports = module.exports = HTMLCov; 1868 | 1869 | /** 1870 | * Initialize a new `JsCoverage` reporter. 1871 | * 1872 | * @param {Runner} runner 1873 | * @api public 1874 | */ 1875 | 1876 | function HTMLCov(runner) { 1877 | var jade = require('jade') 1878 | , file = __dirname + '/templates/coverage.jade' 1879 | , str = fs.readFileSync(file, 'utf8') 1880 | , fn = jade.compile(str, { filename: file }) 1881 | , self = this; 1882 | 1883 | JSONCov.call(this, runner, false); 1884 | 1885 | runner.on('end', function(){ 1886 | process.stdout.write(fn({ 1887 | cov: self.cov 1888 | , coverageClass: coverageClass 1889 | })); 1890 | }); 1891 | } 1892 | 1893 | /** 1894 | * Return coverage class for `n`. 1895 | * 1896 | * @return {String} 1897 | * @api private 1898 | */ 1899 | 1900 | function coverageClass(n) { 1901 | if (n >= 75) return 'high'; 1902 | if (n >= 50) return 'medium'; 1903 | if (n >= 25) return 'low'; 1904 | return 'terrible'; 1905 | } 1906 | }); // module: reporters/html-cov.js 1907 | 1908 | require.register("reporters/html.js", function(module, exports, require){ 1909 | 1910 | /** 1911 | * Module dependencies. 1912 | */ 1913 | 1914 | var Base = require('./base') 1915 | , utils = require('../utils') 1916 | , Progress = require('../browser/progress') 1917 | , escape = utils.escape; 1918 | 1919 | /** 1920 | * Save timer references to avoid Sinon interfering (see GH-237). 1921 | */ 1922 | 1923 | var Date = global.Date 1924 | , setTimeout = global.setTimeout 1925 | , setInterval = global.setInterval 1926 | , clearTimeout = global.clearTimeout 1927 | , clearInterval = global.clearInterval; 1928 | 1929 | /** 1930 | * Expose `Doc`. 1931 | */ 1932 | 1933 | exports = module.exports = HTML; 1934 | 1935 | /** 1936 | * Stats template. 1937 | */ 1938 | 1939 | var statsTemplate = '' 1940 | + '' 1941 | + 'passes: 0' 1942 | + 'failures: 0' 1943 | + 'duration: 0s' 1944 | + ''; 1945 | 1946 | /** 1947 | * Initialize a new `Doc` reporter. 1948 | * 1949 | * @param {Runner} runner 1950 | * @api public 1951 | */ 1952 | 1953 | function HTML(runner, root) { 1954 | Base.call(this, runner); 1955 | 1956 | var self = this 1957 | , stats = this.stats 1958 | , total = runner.total 1959 | , stat = fragment(statsTemplate) 1960 | , items = stat.getElementsByTagName('li') 1961 | , passes = items[1].getElementsByTagName('em')[0] 1962 | , passesLink = items[1].getElementsByTagName('a')[0] 1963 | , failures = items[2].getElementsByTagName('em')[0] 1964 | , failuresLink = items[2].getElementsByTagName('a')[0] 1965 | , duration = items[3].getElementsByTagName('em')[0] 1966 | , canvas = stat.getElementsByTagName('canvas')[0] 1967 | , report = fragment('') 1968 | , stack = [report] 1969 | , progress 1970 | , ctx 1971 | 1972 | root = root || document.getElementById('mocha'); 1973 | 1974 | if (canvas.getContext) { 1975 | var ratio = window.devicePixelRatio || 1; 1976 | canvas.style.width = canvas.width; 1977 | canvas.style.height = canvas.height; 1978 | canvas.width *= ratio; 1979 | canvas.height *= ratio; 1980 | ctx = canvas.getContext('2d'); 1981 | ctx.scale(ratio, ratio); 1982 | progress = new Progress; 1983 | } 1984 | 1985 | if (!root) return error('#mocha div missing, add it to your document'); 1986 | 1987 | // pass toggle 1988 | on(passesLink, 'click', function(){ 1989 | unhide(); 1990 | var name = /pass/.test(report.className) ? '' : ' pass'; 1991 | report.className = report.className.replace(/fail|pass/g, '') + name; 1992 | if (report.className.trim()) hideSuitesWithout('test pass'); 1993 | }); 1994 | 1995 | // failure toggle 1996 | on(failuresLink, 'click', function(){ 1997 | unhide(); 1998 | var name = /fail/.test(report.className) ? '' : ' fail'; 1999 | report.className = report.className.replace(/fail|pass/g, '') + name; 2000 | if (report.className.trim()) hideSuitesWithout('test fail'); 2001 | }); 2002 | 2003 | root.appendChild(stat); 2004 | root.appendChild(report); 2005 | 2006 | if (progress) progress.size(40); 2007 | 2008 | runner.on('suite', function(suite){ 2009 | if (suite.root) return; 2010 | 2011 | // suite 2012 | var url = '?grep=' + encodeURIComponent(suite.fullTitle()); 2013 | var el = fragment('%s', url, escape(suite.title)); 2014 | 2015 | // container 2016 | stack[0].appendChild(el); 2017 | stack.unshift(document.createElement('ul')); 2018 | el.appendChild(stack[0]); 2019 | }); 2020 | 2021 | runner.on('suite end', function(suite){ 2022 | if (suite.root) return; 2023 | stack.shift(); 2024 | }); 2025 | 2026 | runner.on('fail', function(test, err){ 2027 | if ('hook' == test.type) runner.emit('test end', test); 2028 | }); 2029 | 2030 | runner.on('test end', function(test){ 2031 | window.scrollTo(0, document.body.scrollHeight); 2032 | 2033 | // TODO: add to stats 2034 | var percent = stats.tests / this.total * 100 | 0; 2035 | if (progress) progress.update(percent).draw(ctx); 2036 | 2037 | // update stats 2038 | var ms = new Date - stats.start; 2039 | text(passes, stats.passes); 2040 | text(failures, stats.failures); 2041 | text(duration, (ms / 1000).toFixed(2)); 2042 | 2043 | // test 2044 | if ('passed' == test.state) { 2045 | var el = fragment('%e%ems ‣', test.speed, test.title, test.duration, encodeURIComponent(test.fullTitle())); 2046 | } else if (test.pending) { 2047 | var el = fragment('%e', test.title); 2048 | } else { 2049 | var el = fragment('%e ‣', test.title, encodeURIComponent(test.fullTitle())); 2050 | var str = test.err.stack || test.err.toString(); 2051 | 2052 | // FF / Opera do not add the message 2053 | if (!~str.indexOf(test.err.message)) { 2054 | str = test.err.message + '\n' + str; 2055 | } 2056 | 2057 | // <=IE7 stringifies to [Object Error]. Since it can be overloaded, we 2058 | // check for the result of the stringifying. 2059 | if ('[object Error]' == str) str = test.err.message; 2060 | 2061 | // Safari doesn't give you a stack. Let's at least provide a source line. 2062 | if (!test.err.stack && test.err.sourceURL && test.err.line !== undefined) { 2063 | str += "\n(" + test.err.sourceURL + ":" + test.err.line + ")"; 2064 | } 2065 | 2066 | el.appendChild(fragment('%e', str)); 2067 | } 2068 | 2069 | // toggle code 2070 | // TODO: defer 2071 | if (!test.pending) { 2072 | var h2 = el.getElementsByTagName('h2')[0]; 2073 | 2074 | on(h2, 'click', function(){ 2075 | pre.style.display = 'none' == pre.style.display 2076 | ? 'inline-block' 2077 | : 'none'; 2078 | }); 2079 | 2080 | var pre = fragment('%e', utils.clean(test.fn.toString())); 2081 | el.appendChild(pre); 2082 | pre.style.display = 'none'; 2083 | } 2084 | 2085 | // Don't call .appendChild if #mocha-report was already .shift()'ed off the stack. 2086 | if (stack[0]) stack[0].appendChild(el); 2087 | }); 2088 | } 2089 | 2090 | /** 2091 | * Display error `msg`. 2092 | */ 2093 | 2094 | function error(msg) { 2095 | document.body.appendChild(fragment('%s', msg)); 2096 | } 2097 | 2098 | /** 2099 | * Return a DOM fragment from `html`. 2100 | */ 2101 | 2102 | function fragment(html) { 2103 | var args = arguments 2104 | , div = document.createElement('div') 2105 | , i = 1; 2106 | 2107 | div.innerHTML = html.replace(/%([se])/g, function(_, type){ 2108 | switch (type) { 2109 | case 's': return String(args[i++]); 2110 | case 'e': return escape(args[i++]); 2111 | } 2112 | }); 2113 | 2114 | return div.firstChild; 2115 | } 2116 | 2117 | /** 2118 | * Check for suites that do not have elements 2119 | * with `classname`, and hide them. 2120 | */ 2121 | 2122 | function hideSuitesWithout(classname) { 2123 | var suites = document.getElementsByClassName('suite'); 2124 | for (var i = 0; i < suites.length; i++) { 2125 | var els = suites[i].getElementsByClassName(classname); 2126 | if (0 == els.length) suites[i].className += ' hidden'; 2127 | } 2128 | } 2129 | 2130 | /** 2131 | * Unhide .hidden suites. 2132 | */ 2133 | 2134 | function unhide() { 2135 | var els = document.getElementsByClassName('suite hidden'); 2136 | for (var i = 0; i < els.length; ++i) { 2137 | els[i].className = els[i].className.replace('suite hidden', 'suite'); 2138 | } 2139 | } 2140 | 2141 | /** 2142 | * Set `el` text to `str`. 2143 | */ 2144 | 2145 | function text(el, str) { 2146 | if (el.textContent) { 2147 | el.textContent = str; 2148 | } else { 2149 | el.innerText = str; 2150 | } 2151 | } 2152 | 2153 | /** 2154 | * Listen on `event` with callback `fn`. 2155 | */ 2156 | 2157 | function on(el, event, fn) { 2158 | if (el.addEventListener) { 2159 | el.addEventListener(event, fn, false); 2160 | } else { 2161 | el.attachEvent('on' + event, fn); 2162 | } 2163 | } 2164 | 2165 | }); // module: reporters/html.js 2166 | 2167 | require.register("reporters/index.js", function(module, exports, require){ 2168 | 2169 | exports.Base = require('./base'); 2170 | exports.Dot = require('./dot'); 2171 | exports.Doc = require('./doc'); 2172 | exports.TAP = require('./tap'); 2173 | exports.JSON = require('./json'); 2174 | exports.HTML = require('./html'); 2175 | exports.List = require('./list'); 2176 | exports.Min = require('./min'); 2177 | exports.Spec = require('./spec'); 2178 | exports.Nyan = require('./nyan'); 2179 | exports.XUnit = require('./xunit'); 2180 | exports.Markdown = require('./markdown'); 2181 | exports.Progress = require('./progress'); 2182 | exports.Landing = require('./landing'); 2183 | exports.JSONCov = require('./json-cov'); 2184 | exports.HTMLCov = require('./html-cov'); 2185 | exports.JSONStream = require('./json-stream'); 2186 | exports.Teamcity = require('./teamcity'); 2187 | 2188 | }); // module: reporters/index.js 2189 | 2190 | require.register("reporters/json-cov.js", function(module, exports, require){ 2191 | 2192 | /** 2193 | * Module dependencies. 2194 | */ 2195 | 2196 | var Base = require('./base'); 2197 | 2198 | /** 2199 | * Expose `JSONCov`. 2200 | */ 2201 | 2202 | exports = module.exports = JSONCov; 2203 | 2204 | /** 2205 | * Initialize a new `JsCoverage` reporter. 2206 | * 2207 | * @param {Runner} runner 2208 | * @param {Boolean} output 2209 | * @api public 2210 | */ 2211 | 2212 | function JSONCov(runner, output) { 2213 | var self = this 2214 | , output = 1 == arguments.length ? true : output; 2215 | 2216 | Base.call(this, runner); 2217 | 2218 | var tests = [] 2219 | , failures = [] 2220 | , passes = []; 2221 | 2222 | runner.on('test end', function(test){ 2223 | tests.push(test); 2224 | }); 2225 | 2226 | runner.on('pass', function(test){ 2227 | passes.push(test); 2228 | }); 2229 | 2230 | runner.on('fail', function(test){ 2231 | failures.push(test); 2232 | }); 2233 | 2234 | runner.on('end', function(){ 2235 | var cov = global._$jscoverage || {}; 2236 | var result = self.cov = map(cov); 2237 | result.stats = self.stats; 2238 | result.tests = tests.map(clean); 2239 | result.failures = failures.map(clean); 2240 | result.passes = passes.map(clean); 2241 | if (!output) return; 2242 | process.stdout.write(JSON.stringify(result, null, 2 )); 2243 | }); 2244 | } 2245 | 2246 | /** 2247 | * Map jscoverage data to a JSON structure 2248 | * suitable for reporting. 2249 | * 2250 | * @param {Object} cov 2251 | * @return {Object} 2252 | * @api private 2253 | */ 2254 | 2255 | function map(cov) { 2256 | var ret = { 2257 | instrumentation: 'node-jscoverage' 2258 | , sloc: 0 2259 | , hits: 0 2260 | , misses: 0 2261 | , coverage: 0 2262 | , files: [] 2263 | }; 2264 | 2265 | for (var filename in cov) { 2266 | var data = coverage(filename, cov[filename]); 2267 | ret.files.push(data); 2268 | ret.hits += data.hits; 2269 | ret.misses += data.misses; 2270 | ret.sloc += data.sloc; 2271 | } 2272 | 2273 | ret.files.sort(function(a, b) { 2274 | return a.filename.localeCompare(b.filename); 2275 | }); 2276 | 2277 | if (ret.sloc > 0) { 2278 | ret.coverage = (ret.hits / ret.sloc) * 100; 2279 | } 2280 | 2281 | return ret; 2282 | }; 2283 | 2284 | /** 2285 | * Map jscoverage data for a single source file 2286 | * to a JSON structure suitable for reporting. 2287 | * 2288 | * @param {String} filename name of the source file 2289 | * @param {Object} data jscoverage coverage data 2290 | * @return {Object} 2291 | * @api private 2292 | */ 2293 | 2294 | function coverage(filename, data) { 2295 | var ret = { 2296 | filename: filename, 2297 | coverage: 0, 2298 | hits: 0, 2299 | misses: 0, 2300 | sloc: 0, 2301 | source: {} 2302 | }; 2303 | 2304 | data.source.forEach(function(line, num){ 2305 | num++; 2306 | 2307 | if (data[num] === 0) { 2308 | ret.misses++; 2309 | ret.sloc++; 2310 | } else if (data[num] !== undefined) { 2311 | ret.hits++; 2312 | ret.sloc++; 2313 | } 2314 | 2315 | ret.source[num] = { 2316 | source: line 2317 | , coverage: data[num] === undefined 2318 | ? '' 2319 | : data[num] 2320 | }; 2321 | }); 2322 | 2323 | ret.coverage = ret.hits / ret.sloc * 100; 2324 | 2325 | return ret; 2326 | } 2327 | 2328 | /** 2329 | * Return a plain-object representation of `test` 2330 | * free of cyclic properties etc. 2331 | * 2332 | * @param {Object} test 2333 | * @return {Object} 2334 | * @api private 2335 | */ 2336 | 2337 | function clean(test) { 2338 | return { 2339 | title: test.title 2340 | , fullTitle: test.fullTitle() 2341 | , duration: test.duration 2342 | } 2343 | } 2344 | 2345 | }); // module: reporters/json-cov.js 2346 | 2347 | require.register("reporters/json-stream.js", function(module, exports, require){ 2348 | 2349 | /** 2350 | * Module dependencies. 2351 | */ 2352 | 2353 | var Base = require('./base') 2354 | , color = Base.color; 2355 | 2356 | /** 2357 | * Expose `List`. 2358 | */ 2359 | 2360 | exports = module.exports = List; 2361 | 2362 | /** 2363 | * Initialize a new `List` test reporter. 2364 | * 2365 | * @param {Runner} runner 2366 | * @api public 2367 | */ 2368 | 2369 | function List(runner) { 2370 | Base.call(this, runner); 2371 | 2372 | var self = this 2373 | , stats = this.stats 2374 | , total = runner.total; 2375 | 2376 | runner.on('start', function(){ 2377 | console.log(JSON.stringify(['start', { total: total }])); 2378 | }); 2379 | 2380 | runner.on('pass', function(test){ 2381 | console.log(JSON.stringify(['pass', clean(test)])); 2382 | }); 2383 | 2384 | runner.on('fail', function(test, err){ 2385 | console.log(JSON.stringify(['fail', clean(test)])); 2386 | }); 2387 | 2388 | runner.on('end', function(){ 2389 | process.stdout.write(JSON.stringify(['end', self.stats])); 2390 | }); 2391 | } 2392 | 2393 | /** 2394 | * Return a plain-object representation of `test` 2395 | * free of cyclic properties etc. 2396 | * 2397 | * @param {Object} test 2398 | * @return {Object} 2399 | * @api private 2400 | */ 2401 | 2402 | function clean(test) { 2403 | return { 2404 | title: test.title 2405 | , fullTitle: test.fullTitle() 2406 | , duration: test.duration 2407 | } 2408 | } 2409 | }); // module: reporters/json-stream.js 2410 | 2411 | require.register("reporters/json.js", function(module, exports, require){ 2412 | 2413 | /** 2414 | * Module dependencies. 2415 | */ 2416 | 2417 | var Base = require('./base') 2418 | , cursor = Base.cursor 2419 | , color = Base.color; 2420 | 2421 | /** 2422 | * Expose `JSON`. 2423 | */ 2424 | 2425 | exports = module.exports = JSONReporter; 2426 | 2427 | /** 2428 | * Initialize a new `JSON` reporter. 2429 | * 2430 | * @param {Runner} runner 2431 | * @api public 2432 | */ 2433 | 2434 | function JSONReporter(runner) { 2435 | var self = this; 2436 | Base.call(this, runner); 2437 | 2438 | var tests = [] 2439 | , failures = [] 2440 | , passes = []; 2441 | 2442 | runner.on('test end', function(test){ 2443 | tests.push(test); 2444 | }); 2445 | 2446 | runner.on('pass', function(test){ 2447 | passes.push(test); 2448 | }); 2449 | 2450 | runner.on('fail', function(test){ 2451 | failures.push(test); 2452 | }); 2453 | 2454 | runner.on('end', function(){ 2455 | var obj = { 2456 | stats: self.stats 2457 | , tests: tests.map(clean) 2458 | , failures: failures.map(clean) 2459 | , passes: passes.map(clean) 2460 | }; 2461 | 2462 | process.stdout.write(JSON.stringify(obj, null, 2)); 2463 | }); 2464 | } 2465 | 2466 | /** 2467 | * Return a plain-object representation of `test` 2468 | * free of cyclic properties etc. 2469 | * 2470 | * @param {Object} test 2471 | * @return {Object} 2472 | * @api private 2473 | */ 2474 | 2475 | function clean(test) { 2476 | return { 2477 | title: test.title 2478 | , fullTitle: test.fullTitle() 2479 | , duration: test.duration 2480 | } 2481 | } 2482 | }); // module: reporters/json.js 2483 | 2484 | require.register("reporters/landing.js", function(module, exports, require){ 2485 | 2486 | /** 2487 | * Module dependencies. 2488 | */ 2489 | 2490 | var Base = require('./base') 2491 | , cursor = Base.cursor 2492 | , color = Base.color; 2493 | 2494 | /** 2495 | * Expose `Landing`. 2496 | */ 2497 | 2498 | exports = module.exports = Landing; 2499 | 2500 | /** 2501 | * Airplane color. 2502 | */ 2503 | 2504 | Base.colors.plane = 0; 2505 | 2506 | /** 2507 | * Airplane crash color. 2508 | */ 2509 | 2510 | Base.colors['plane crash'] = 31; 2511 | 2512 | /** 2513 | * Runway color. 2514 | */ 2515 | 2516 | Base.colors.runway = 90; 2517 | 2518 | /** 2519 | * Initialize a new `Landing` reporter. 2520 | * 2521 | * @param {Runner} runner 2522 | * @api public 2523 | */ 2524 | 2525 | function Landing(runner) { 2526 | Base.call(this, runner); 2527 | 2528 | var self = this 2529 | , stats = this.stats 2530 | , width = Base.window.width * .75 | 0 2531 | , total = runner.total 2532 | , stream = process.stdout 2533 | , plane = color('plane', '✈') 2534 | , crashed = -1 2535 | , n = 0; 2536 | 2537 | function runway() { 2538 | var buf = Array(width).join('-'); 2539 | return ' ' + color('runway', buf); 2540 | } 2541 | 2542 | runner.on('start', function(){ 2543 | stream.write('\n '); 2544 | cursor.hide(); 2545 | }); 2546 | 2547 | runner.on('test end', function(test){ 2548 | // check if the plane crashed 2549 | var col = -1 == crashed 2550 | ? width * ++n / total | 0 2551 | : crashed; 2552 | 2553 | // show the crash 2554 | if ('failed' == test.state) { 2555 | plane = color('plane crash', '✈'); 2556 | crashed = col; 2557 | } 2558 | 2559 | // render landing strip 2560 | stream.write('\u001b[4F\n\n'); 2561 | stream.write(runway()); 2562 | stream.write('\n '); 2563 | stream.write(color('runway', Array(col).join('⋅'))); 2564 | stream.write(plane) 2565 | stream.write(color('runway', Array(width - col).join('⋅') + '\n')); 2566 | stream.write(runway()); 2567 | stream.write('\u001b[0m'); 2568 | }); 2569 | 2570 | runner.on('end', function(){ 2571 | cursor.show(); 2572 | console.log(); 2573 | self.epilogue(); 2574 | }); 2575 | } 2576 | 2577 | /** 2578 | * Inherit from `Base.prototype`. 2579 | */ 2580 | 2581 | Landing.prototype = new Base; 2582 | Landing.prototype.constructor = Landing; 2583 | 2584 | }); // module: reporters/landing.js 2585 | 2586 | require.register("reporters/list.js", function(module, exports, require){ 2587 | 2588 | /** 2589 | * Module dependencies. 2590 | */ 2591 | 2592 | var Base = require('./base') 2593 | , cursor = Base.cursor 2594 | , color = Base.color; 2595 | 2596 | /** 2597 | * Expose `List`. 2598 | */ 2599 | 2600 | exports = module.exports = List; 2601 | 2602 | /** 2603 | * Initialize a new `List` test reporter. 2604 | * 2605 | * @param {Runner} runner 2606 | * @api public 2607 | */ 2608 | 2609 | function List(runner) { 2610 | Base.call(this, runner); 2611 | 2612 | var self = this 2613 | , stats = this.stats 2614 | , n = 0; 2615 | 2616 | runner.on('start', function(){ 2617 | console.log(); 2618 | }); 2619 | 2620 | runner.on('test', function(test){ 2621 | process.stdout.write(color('pass', ' ' + test.fullTitle() + ': ')); 2622 | }); 2623 | 2624 | runner.on('pending', function(test){ 2625 | var fmt = color('checkmark', ' -') 2626 | + color('pending', ' %s'); 2627 | console.log(fmt, test.fullTitle()); 2628 | }); 2629 | 2630 | runner.on('pass', function(test){ 2631 | var fmt = color('checkmark', ' '+Base.symbols.dot) 2632 | + color('pass', ' %s: ') 2633 | + color(test.speed, '%dms'); 2634 | cursor.CR(); 2635 | console.log(fmt, test.fullTitle(), test.duration); 2636 | }); 2637 | 2638 | runner.on('fail', function(test, err){ 2639 | cursor.CR(); 2640 | console.log(color('fail', ' %d) %s'), ++n, test.fullTitle()); 2641 | }); 2642 | 2643 | runner.on('end', self.epilogue.bind(self)); 2644 | } 2645 | 2646 | /** 2647 | * Inherit from `Base.prototype`. 2648 | */ 2649 | 2650 | List.prototype = new Base; 2651 | List.prototype.constructor = List; 2652 | 2653 | 2654 | }); // module: reporters/list.js 2655 | 2656 | require.register("reporters/markdown.js", function(module, exports, require){ 2657 | /** 2658 | * Module dependencies. 2659 | */ 2660 | 2661 | var Base = require('./base') 2662 | , utils = require('../utils'); 2663 | 2664 | /** 2665 | * Expose `Markdown`. 2666 | */ 2667 | 2668 | exports = module.exports = Markdown; 2669 | 2670 | /** 2671 | * Initialize a new `Markdown` reporter. 2672 | * 2673 | * @param {Runner} runner 2674 | * @api public 2675 | */ 2676 | 2677 | function Markdown(runner) { 2678 | Base.call(this, runner); 2679 | 2680 | var self = this 2681 | , stats = this.stats 2682 | , total = runner.total 2683 | , level = 0 2684 | , buf = ''; 2685 | 2686 | function title(str) { 2687 | return Array(level).join('#') + ' ' + str; 2688 | } 2689 | 2690 | function indent() { 2691 | return Array(level).join(' '); 2692 | } 2693 | 2694 | function mapTOC(suite, obj) { 2695 | var ret = obj; 2696 | obj = obj[suite.title] = obj[suite.title] || { suite: suite }; 2697 | suite.suites.forEach(function(suite){ 2698 | mapTOC(suite, obj); 2699 | }); 2700 | return ret; 2701 | } 2702 | 2703 | function stringifyTOC(obj, level) { 2704 | ++level; 2705 | var buf = ''; 2706 | var link; 2707 | for (var key in obj) { 2708 | if ('suite' == key) continue; 2709 | if (key) link = ' - [' + key + '](#' + utils.slug(obj[key].suite.fullTitle()) + ')\n'; 2710 | if (key) buf += Array(level).join(' ') + link; 2711 | buf += stringifyTOC(obj[key], level); 2712 | } 2713 | --level; 2714 | return buf; 2715 | } 2716 | 2717 | function generateTOC(suite) { 2718 | var obj = mapTOC(suite, {}); 2719 | return stringifyTOC(obj, 0); 2720 | } 2721 | 2722 | generateTOC(runner.suite); 2723 | 2724 | runner.on('suite', function(suite){ 2725 | ++level; 2726 | var slug = utils.slug(suite.fullTitle()); 2727 | buf += '' + '\n'; 2728 | buf += title(suite.title) + '\n'; 2729 | }); 2730 | 2731 | runner.on('suite end', function(suite){ 2732 | --level; 2733 | }); 2734 | 2735 | runner.on('pass', function(test){ 2736 | var code = utils.clean(test.fn.toString()); 2737 | buf += test.title + '.\n'; 2738 | buf += '\n```js\n'; 2739 | buf += code + '\n'; 2740 | buf += '```\n\n'; 2741 | }); 2742 | 2743 | runner.on('end', function(){ 2744 | process.stdout.write('# TOC\n'); 2745 | process.stdout.write(generateTOC(runner.suite)); 2746 | process.stdout.write(buf); 2747 | }); 2748 | } 2749 | }); // module: reporters/markdown.js 2750 | 2751 | require.register("reporters/min.js", function(module, exports, require){ 2752 | 2753 | /** 2754 | * Module dependencies. 2755 | */ 2756 | 2757 | var Base = require('./base'); 2758 | 2759 | /** 2760 | * Expose `Min`. 2761 | */ 2762 | 2763 | exports = module.exports = Min; 2764 | 2765 | /** 2766 | * Initialize a new `Min` minimal test reporter (best used with --watch). 2767 | * 2768 | * @param {Runner} runner 2769 | * @api public 2770 | */ 2771 | 2772 | function Min(runner) { 2773 | Base.call(this, runner); 2774 | 2775 | runner.on('start', function(){ 2776 | // clear screen 2777 | process.stdout.write('\u001b[2J'); 2778 | // set cursor position 2779 | process.stdout.write('\u001b[1;3H'); 2780 | }); 2781 | 2782 | runner.on('end', this.epilogue.bind(this)); 2783 | } 2784 | 2785 | /** 2786 | * Inherit from `Base.prototype`. 2787 | */ 2788 | 2789 | Min.prototype = new Base; 2790 | Min.prototype.constructor = Min; 2791 | 2792 | }); // module: reporters/min.js 2793 | 2794 | require.register("reporters/nyan.js", function(module, exports, require){ 2795 | 2796 | /** 2797 | * Module dependencies. 2798 | */ 2799 | 2800 | var Base = require('./base') 2801 | , color = Base.color; 2802 | 2803 | /** 2804 | * Expose `Dot`. 2805 | */ 2806 | 2807 | exports = module.exports = NyanCat; 2808 | 2809 | /** 2810 | * Initialize a new `Dot` matrix test reporter. 2811 | * 2812 | * @param {Runner} runner 2813 | * @api public 2814 | */ 2815 | 2816 | function NyanCat(runner) { 2817 | Base.call(this, runner); 2818 | 2819 | var self = this 2820 | , stats = this.stats 2821 | , width = Base.window.width * .75 | 0 2822 | , rainbowColors = this.rainbowColors = self.generateColors() 2823 | , colorIndex = this.colorIndex = 0 2824 | , numerOfLines = this.numberOfLines = 4 2825 | , trajectories = this.trajectories = [[], [], [], []] 2826 | , nyanCatWidth = this.nyanCatWidth = 11 2827 | , trajectoryWidthMax = this.trajectoryWidthMax = (width - nyanCatWidth) 2828 | , scoreboardWidth = this.scoreboardWidth = 5 2829 | , tick = this.tick = 0 2830 | , n = 0; 2831 | 2832 | runner.on('start', function(){ 2833 | Base.cursor.hide(); 2834 | self.draw('start'); 2835 | }); 2836 | 2837 | runner.on('pending', function(test){ 2838 | self.draw('pending'); 2839 | }); 2840 | 2841 | runner.on('pass', function(test){ 2842 | self.draw('pass'); 2843 | }); 2844 | 2845 | runner.on('fail', function(test, err){ 2846 | self.draw('fail'); 2847 | }); 2848 | 2849 | runner.on('end', function(){ 2850 | Base.cursor.show(); 2851 | for (var i = 0; i < self.numberOfLines; i++) write('\n'); 2852 | self.epilogue(); 2853 | }); 2854 | } 2855 | 2856 | /** 2857 | * Draw the nyan cat with runner `status`. 2858 | * 2859 | * @param {String} status 2860 | * @api private 2861 | */ 2862 | 2863 | NyanCat.prototype.draw = function(status){ 2864 | this.appendRainbow(); 2865 | this.drawScoreboard(); 2866 | this.drawRainbow(); 2867 | this.drawNyanCat(status); 2868 | this.tick = !this.tick; 2869 | }; 2870 | 2871 | /** 2872 | * Draw the "scoreboard" showing the number 2873 | * of passes, failures and pending tests. 2874 | * 2875 | * @api private 2876 | */ 2877 | 2878 | NyanCat.prototype.drawScoreboard = function(){ 2879 | var stats = this.stats; 2880 | var colors = Base.colors; 2881 | 2882 | function draw(color, n) { 2883 | write(' '); 2884 | write('\u001b[' + color + 'm' + n + '\u001b[0m'); 2885 | write('\n'); 2886 | } 2887 | 2888 | draw(colors.green, stats.passes); 2889 | draw(colors.fail, stats.failures); 2890 | draw(colors.pending, stats.pending); 2891 | write('\n'); 2892 | 2893 | this.cursorUp(this.numberOfLines); 2894 | }; 2895 | 2896 | /** 2897 | * Append the rainbow. 2898 | * 2899 | * @api private 2900 | */ 2901 | 2902 | NyanCat.prototype.appendRainbow = function(){ 2903 | var segment = this.tick ? '_' : '-'; 2904 | var rainbowified = this.rainbowify(segment); 2905 | 2906 | for (var index = 0; index < this.numberOfLines; index++) { 2907 | var trajectory = this.trajectories[index]; 2908 | if (trajectory.length >= this.trajectoryWidthMax) trajectory.shift(); 2909 | trajectory.push(rainbowified); 2910 | } 2911 | }; 2912 | 2913 | /** 2914 | * Draw the rainbow. 2915 | * 2916 | * @api private 2917 | */ 2918 | 2919 | NyanCat.prototype.drawRainbow = function(){ 2920 | var self = this; 2921 | 2922 | this.trajectories.forEach(function(line, index) { 2923 | write('\u001b[' + self.scoreboardWidth + 'C'); 2924 | write(line.join('')); 2925 | write('\n'); 2926 | }); 2927 | 2928 | this.cursorUp(this.numberOfLines); 2929 | }; 2930 | 2931 | /** 2932 | * Draw the nyan cat with `status`. 2933 | * 2934 | * @param {String} status 2935 | * @api private 2936 | */ 2937 | 2938 | NyanCat.prototype.drawNyanCat = function(status) { 2939 | var self = this; 2940 | var startWidth = this.scoreboardWidth + this.trajectories[0].length; 2941 | 2942 | [0, 1, 2, 3].forEach(function(index) { 2943 | write('\u001b[' + startWidth + 'C'); 2944 | 2945 | switch (index) { 2946 | case 0: 2947 | write('_,------,'); 2948 | write('\n'); 2949 | break; 2950 | case 1: 2951 | var padding = self.tick ? ' ' : ' '; 2952 | write('_|' + padding + '/\\_/\\ '); 2953 | write('\n'); 2954 | break; 2955 | case 2: 2956 | var padding = self.tick ? '_' : '__'; 2957 | var tail = self.tick ? '~' : '^'; 2958 | var face; 2959 | switch (status) { 2960 | case 'pass': 2961 | face = '( ^ .^)'; 2962 | break; 2963 | case 'fail': 2964 | face = '( o .o)'; 2965 | break; 2966 | default: 2967 | face = '( - .-)'; 2968 | } 2969 | write(tail + '|' + padding + face + ' '); 2970 | write('\n'); 2971 | break; 2972 | case 3: 2973 | var padding = self.tick ? ' ' : ' '; 2974 | write(padding + '"" "" '); 2975 | write('\n'); 2976 | break; 2977 | } 2978 | }); 2979 | 2980 | this.cursorUp(this.numberOfLines); 2981 | }; 2982 | 2983 | /** 2984 | * Move cursor up `n`. 2985 | * 2986 | * @param {Number} n 2987 | * @api private 2988 | */ 2989 | 2990 | NyanCat.prototype.cursorUp = function(n) { 2991 | write('\u001b[' + n + 'A'); 2992 | }; 2993 | 2994 | /** 2995 | * Move cursor down `n`. 2996 | * 2997 | * @param {Number} n 2998 | * @api private 2999 | */ 3000 | 3001 | NyanCat.prototype.cursorDown = function(n) { 3002 | write('\u001b[' + n + 'B'); 3003 | }; 3004 | 3005 | /** 3006 | * Generate rainbow colors. 3007 | * 3008 | * @return {Array} 3009 | * @api private 3010 | */ 3011 | 3012 | NyanCat.prototype.generateColors = function(){ 3013 | var colors = []; 3014 | 3015 | for (var i = 0; i < (6 * 7); i++) { 3016 | var pi3 = Math.floor(Math.PI / 3); 3017 | var n = (i * (1.0 / 6)); 3018 | var r = Math.floor(3 * Math.sin(n) + 3); 3019 | var g = Math.floor(3 * Math.sin(n + 2 * pi3) + 3); 3020 | var b = Math.floor(3 * Math.sin(n + 4 * pi3) + 3); 3021 | colors.push(36 * r + 6 * g + b + 16); 3022 | } 3023 | 3024 | return colors; 3025 | }; 3026 | 3027 | /** 3028 | * Apply rainbow to the given `str`. 3029 | * 3030 | * @param {String} str 3031 | * @return {String} 3032 | * @api private 3033 | */ 3034 | 3035 | NyanCat.prototype.rainbowify = function(str){ 3036 | var color = this.rainbowColors[this.colorIndex % this.rainbowColors.length]; 3037 | this.colorIndex += 1; 3038 | return '\u001b[38;5;' + color + 'm' + str + '\u001b[0m'; 3039 | }; 3040 | 3041 | /** 3042 | * Stdout helper. 3043 | */ 3044 | 3045 | function write(string) { 3046 | process.stdout.write(string); 3047 | } 3048 | 3049 | /** 3050 | * Inherit from `Base.prototype`. 3051 | */ 3052 | 3053 | NyanCat.prototype = new Base; 3054 | NyanCat.prototype.constructor = NyanCat; 3055 | 3056 | 3057 | }); // module: reporters/nyan.js 3058 | 3059 | require.register("reporters/progress.js", function(module, exports, require){ 3060 | 3061 | /** 3062 | * Module dependencies. 3063 | */ 3064 | 3065 | var Base = require('./base') 3066 | , cursor = Base.cursor 3067 | , color = Base.color; 3068 | 3069 | /** 3070 | * Expose `Progress`. 3071 | */ 3072 | 3073 | exports = module.exports = Progress; 3074 | 3075 | /** 3076 | * General progress bar color. 3077 | */ 3078 | 3079 | Base.colors.progress = 90; 3080 | 3081 | /** 3082 | * Initialize a new `Progress` bar test reporter. 3083 | * 3084 | * @param {Runner} runner 3085 | * @param {Object} options 3086 | * @api public 3087 | */ 3088 | 3089 | function Progress(runner, options) { 3090 | Base.call(this, runner); 3091 | 3092 | var self = this 3093 | , options = options || {} 3094 | , stats = this.stats 3095 | , width = Base.window.width * .50 | 0 3096 | , total = runner.total 3097 | , complete = 0 3098 | , max = Math.max; 3099 | 3100 | // default chars 3101 | options.open = options.open || '['; 3102 | options.complete = options.complete || '▬'; 3103 | options.incomplete = options.incomplete || Base.symbols.dot; 3104 | options.close = options.close || ']'; 3105 | options.verbose = false; 3106 | 3107 | // tests started 3108 | runner.on('start', function(){ 3109 | console.log(); 3110 | cursor.hide(); 3111 | }); 3112 | 3113 | // tests complete 3114 | runner.on('test end', function(){ 3115 | complete++; 3116 | var incomplete = total - complete 3117 | , percent = complete / total 3118 | , n = width * percent | 0 3119 | , i = width - n; 3120 | 3121 | cursor.CR(); 3122 | process.stdout.write('\u001b[J'); 3123 | process.stdout.write(color('progress', ' ' + options.open)); 3124 | process.stdout.write(Array(n).join(options.complete)); 3125 | process.stdout.write(Array(i).join(options.incomplete)); 3126 | process.stdout.write(color('progress', options.close)); 3127 | if (options.verbose) { 3128 | process.stdout.write(color('progress', ' ' + complete + ' of ' + total)); 3129 | } 3130 | }); 3131 | 3132 | // tests are complete, output some stats 3133 | // and the failures if any 3134 | runner.on('end', function(){ 3135 | cursor.show(); 3136 | console.log(); 3137 | self.epilogue(); 3138 | }); 3139 | } 3140 | 3141 | /** 3142 | * Inherit from `Base.prototype`. 3143 | */ 3144 | 3145 | Progress.prototype = new Base; 3146 | Progress.prototype.constructor = Progress; 3147 | 3148 | 3149 | }); // module: reporters/progress.js 3150 | 3151 | require.register("reporters/spec.js", function(module, exports, require){ 3152 | 3153 | /** 3154 | * Module dependencies. 3155 | */ 3156 | 3157 | var Base = require('./base') 3158 | , cursor = Base.cursor 3159 | , color = Base.color; 3160 | 3161 | /** 3162 | * Expose `Spec`. 3163 | */ 3164 | 3165 | exports = module.exports = Spec; 3166 | 3167 | /** 3168 | * Initialize a new `Spec` test reporter. 3169 | * 3170 | * @param {Runner} runner 3171 | * @api public 3172 | */ 3173 | 3174 | function Spec(runner) { 3175 | Base.call(this, runner); 3176 | 3177 | var self = this 3178 | , stats = this.stats 3179 | , indents = 0 3180 | , n = 0; 3181 | 3182 | function indent() { 3183 | return Array(indents).join(' ') 3184 | } 3185 | 3186 | runner.on('start', function(){ 3187 | console.log(); 3188 | }); 3189 | 3190 | runner.on('suite', function(suite){ 3191 | ++indents; 3192 | console.log(color('suite', '%s%s'), indent(), suite.title); 3193 | }); 3194 | 3195 | runner.on('suite end', function(suite){ 3196 | --indents; 3197 | if (1 == indents) console.log(); 3198 | }); 3199 | 3200 | runner.on('test', function(test){ 3201 | process.stdout.write(indent() + color('pass', ' ◦ ' + test.title + ': ')); 3202 | }); 3203 | 3204 | runner.on('pending', function(test){ 3205 | var fmt = indent() + color('pending', ' - %s'); 3206 | console.log(fmt, test.title); 3207 | }); 3208 | 3209 | runner.on('pass', function(test){ 3210 | if ('fast' == test.speed) { 3211 | var fmt = indent() 3212 | + color('checkmark', ' ' + Base.symbols.ok) 3213 | + color('pass', ' %s '); 3214 | cursor.CR(); 3215 | console.log(fmt, test.title); 3216 | } else { 3217 | var fmt = indent() 3218 | + color('checkmark', ' ' + Base.symbols.ok) 3219 | + color('pass', ' %s ') 3220 | + color(test.speed, '(%dms)'); 3221 | cursor.CR(); 3222 | console.log(fmt, test.title, test.duration); 3223 | } 3224 | }); 3225 | 3226 | runner.on('fail', function(test, err){ 3227 | cursor.CR(); 3228 | console.log(indent() + color('fail', ' %d) %s'), ++n, test.title); 3229 | }); 3230 | 3231 | runner.on('end', self.epilogue.bind(self)); 3232 | } 3233 | 3234 | /** 3235 | * Inherit from `Base.prototype`. 3236 | */ 3237 | 3238 | Spec.prototype = new Base; 3239 | Spec.prototype.constructor = Spec; 3240 | 3241 | 3242 | }); // module: reporters/spec.js 3243 | 3244 | require.register("reporters/tap.js", function(module, exports, require){ 3245 | 3246 | /** 3247 | * Module dependencies. 3248 | */ 3249 | 3250 | var Base = require('./base') 3251 | , cursor = Base.cursor 3252 | , color = Base.color; 3253 | 3254 | /** 3255 | * Expose `TAP`. 3256 | */ 3257 | 3258 | exports = module.exports = TAP; 3259 | 3260 | /** 3261 | * Initialize a new `TAP` reporter. 3262 | * 3263 | * @param {Runner} runner 3264 | * @api public 3265 | */ 3266 | 3267 | function TAP(runner) { 3268 | Base.call(this, runner); 3269 | 3270 | var self = this 3271 | , stats = this.stats 3272 | , n = 1 3273 | , passes = 0 3274 | , failures = 1; 3275 | 3276 | runner.on('start', function(){ 3277 | var total = runner.grepTotal(runner.suite); 3278 | console.log('%d..%d', 1, total); 3279 | }); 3280 | 3281 | runner.on('test end', function(){ 3282 | ++n; 3283 | }); 3284 | 3285 | runner.on('pending', function(test){ 3286 | console.log('ok %d %s # SKIP -', n, title(test)); 3287 | }); 3288 | 3289 | runner.on('pass', function(test){ 3290 | passes++; 3291 | console.log('ok %d %s', n, title(test)); 3292 | }); 3293 | 3294 | runner.on('fail', function(test, err){ 3295 | failures++; 3296 | console.log('not ok %d %s', n, title(test)); 3297 | console.log(err.stack.replace(/^/gm, ' ')); 3298 | }); 3299 | 3300 | runner.on('end', function(){ 3301 | console.log('# tests ' + (passes + failures)); 3302 | console.log('# pass ' + passes); 3303 | console.log('# fail ' + failures); 3304 | }); 3305 | } 3306 | 3307 | /** 3308 | * Return a TAP-safe title of `test` 3309 | * 3310 | * @param {Object} test 3311 | * @return {String} 3312 | * @api private 3313 | */ 3314 | 3315 | function title(test) { 3316 | return test.fullTitle().replace(/#/g, ''); 3317 | } 3318 | 3319 | }); // module: reporters/tap.js 3320 | 3321 | require.register("reporters/teamcity.js", function(module, exports, require){ 3322 | 3323 | /** 3324 | * Module dependencies. 3325 | */ 3326 | 3327 | var Base = require('./base'); 3328 | 3329 | /** 3330 | * Expose `Teamcity`. 3331 | */ 3332 | 3333 | exports = module.exports = Teamcity; 3334 | 3335 | /** 3336 | * Initialize a new `Teamcity` reporter. 3337 | * 3338 | * @param {Runner} runner 3339 | * @api public 3340 | */ 3341 | 3342 | function Teamcity(runner) { 3343 | Base.call(this, runner); 3344 | var stats = this.stats; 3345 | 3346 | runner.on('start', function() { 3347 | console.log("##teamcity[testSuiteStarted name='mocha.suite']"); 3348 | }); 3349 | 3350 | runner.on('test', function(test) { 3351 | console.log("##teamcity[testStarted name='" + escape(test.fullTitle()) + "']"); 3352 | }); 3353 | 3354 | runner.on('fail', function(test, err) { 3355 | console.log("##teamcity[testFailed name='" + escape(test.fullTitle()) + "' message='" + escape(err.message) + "']"); 3356 | }); 3357 | 3358 | runner.on('pending', function(test) { 3359 | console.log("##teamcity[testIgnored name='" + escape(test.fullTitle()) + "' message='pending']"); 3360 | }); 3361 | 3362 | runner.on('test end', function(test) { 3363 | console.log("##teamcity[testFinished name='" + escape(test.fullTitle()) + "' duration='" + test.duration + "']"); 3364 | }); 3365 | 3366 | runner.on('end', function() { 3367 | console.log("##teamcity[testSuiteFinished name='mocha.suite' duration='" + stats.duration + "']"); 3368 | }); 3369 | } 3370 | 3371 | /** 3372 | * Escape the given `str`. 3373 | */ 3374 | 3375 | function escape(str) { 3376 | return str 3377 | .replace(/\|/g, "||") 3378 | .replace(/\n/g, "|n") 3379 | .replace(/\r/g, "|r") 3380 | .replace(/\[/g, "|[") 3381 | .replace(/\]/g, "|]") 3382 | .replace(/\u0085/g, "|x") 3383 | .replace(/\u2028/g, "|l") 3384 | .replace(/\u2029/g, "|p") 3385 | .replace(/'/g, "|'"); 3386 | } 3387 | 3388 | }); // module: reporters/teamcity.js 3389 | 3390 | require.register("reporters/xunit.js", function(module, exports, require){ 3391 | 3392 | /** 3393 | * Module dependencies. 3394 | */ 3395 | 3396 | var Base = require('./base') 3397 | , utils = require('../utils') 3398 | , escape = utils.escape; 3399 | 3400 | /** 3401 | * Save timer references to avoid Sinon interfering (see GH-237). 3402 | */ 3403 | 3404 | var Date = global.Date 3405 | , setTimeout = global.setTimeout 3406 | , setInterval = global.setInterval 3407 | , clearTimeout = global.clearTimeout 3408 | , clearInterval = global.clearInterval; 3409 | 3410 | /** 3411 | * Expose `XUnit`. 3412 | */ 3413 | 3414 | exports = module.exports = XUnit; 3415 | 3416 | /** 3417 | * Initialize a new `XUnit` reporter. 3418 | * 3419 | * @param {Runner} runner 3420 | * @api public 3421 | */ 3422 | 3423 | function XUnit(runner) { 3424 | Base.call(this, runner); 3425 | var stats = this.stats 3426 | , tests = [] 3427 | , self = this; 3428 | 3429 | runner.on('pass', function(test){ 3430 | tests.push(test); 3431 | }); 3432 | 3433 | runner.on('fail', function(test){ 3434 | tests.push(test); 3435 | }); 3436 | 3437 | runner.on('end', function(){ 3438 | console.log(tag('testsuite', { 3439 | name: 'Mocha Tests' 3440 | , tests: stats.tests 3441 | , failures: stats.failures 3442 | , errors: stats.failures 3443 | , skip: stats.tests - stats.failures - stats.passes 3444 | , timestamp: (new Date).toUTCString() 3445 | , time: stats.duration / 1000 3446 | }, false)); 3447 | 3448 | tests.forEach(test); 3449 | console.log(''); 3450 | }); 3451 | } 3452 | 3453 | /** 3454 | * Inherit from `Base.prototype`. 3455 | */ 3456 | 3457 | XUnit.prototype = new Base; 3458 | XUnit.prototype.constructor = XUnit; 3459 | 3460 | 3461 | /** 3462 | * Output tag for the given `test.` 3463 | */ 3464 | 3465 | function test(test) { 3466 | var attrs = { 3467 | classname: test.parent.fullTitle() 3468 | , name: test.title 3469 | , time: test.duration / 1000 3470 | }; 3471 | 3472 | if ('failed' == test.state) { 3473 | var err = test.err; 3474 | attrs.message = escape(err.message); 3475 | console.log(tag('testcase', attrs, false, tag('failure', attrs, false, cdata(err.stack)))); 3476 | } else if (test.pending) { 3477 | console.log(tag('testcase', attrs, false, tag('skipped', {}, true))); 3478 | } else { 3479 | console.log(tag('testcase', attrs, true) ); 3480 | } 3481 | } 3482 | 3483 | /** 3484 | * HTML tag helper. 3485 | */ 3486 | 3487 | function tag(name, attrs, close, content) { 3488 | var end = close ? '/>' : '>' 3489 | , pairs = [] 3490 | , tag; 3491 | 3492 | for (var key in attrs) { 3493 | pairs.push(key + '="' + escape(attrs[key]) + '"'); 3494 | } 3495 | 3496 | tag = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end; 3497 | if (content) tag += content + '' + name + end; 3498 | return tag; 3499 | } 3500 | 3501 | /** 3502 | * Return cdata escaped CDATA `str`. 3503 | */ 3504 | 3505 | function cdata(str) { 3506 | return ''; 3507 | } 3508 | 3509 | }); // module: reporters/xunit.js 3510 | 3511 | require.register("runnable.js", function(module, exports, require){ 3512 | 3513 | /** 3514 | * Module dependencies. 3515 | */ 3516 | 3517 | var EventEmitter = require('browser/events').EventEmitter 3518 | , debug = require('browser/debug')('mocha:runnable') 3519 | , milliseconds = require('./ms'); 3520 | 3521 | /** 3522 | * Save timer references to avoid Sinon interfering (see GH-237). 3523 | */ 3524 | 3525 | var Date = global.Date 3526 | , setTimeout = global.setTimeout 3527 | , setInterval = global.setInterval 3528 | , clearTimeout = global.clearTimeout 3529 | , clearInterval = global.clearInterval; 3530 | 3531 | /** 3532 | * Object#toString(). 3533 | */ 3534 | 3535 | var toString = Object.prototype.toString; 3536 | 3537 | /** 3538 | * Expose `Runnable`. 3539 | */ 3540 | 3541 | module.exports = Runnable; 3542 | 3543 | /** 3544 | * Initialize a new `Runnable` with the given `title` and callback `fn`. 3545 | * 3546 | * @param {String} title 3547 | * @param {Function} fn 3548 | * @api private 3549 | */ 3550 | 3551 | function Runnable(title, fn) { 3552 | this.title = title; 3553 | this.fn = fn; 3554 | this.async = fn && fn.length; 3555 | this.sync = ! this.async; 3556 | this._timeout = 2000; 3557 | this._slow = 75; 3558 | this.timedOut = false; 3559 | } 3560 | 3561 | /** 3562 | * Inherit from `EventEmitter.prototype`. 3563 | */ 3564 | 3565 | Runnable.prototype = new EventEmitter; 3566 | Runnable.prototype.constructor = Runnable; 3567 | 3568 | 3569 | /** 3570 | * Set & get timeout `ms`. 3571 | * 3572 | * @param {Number|String} ms 3573 | * @return {Runnable|Number} ms or self 3574 | * @api private 3575 | */ 3576 | 3577 | Runnable.prototype.timeout = function(ms){ 3578 | if (0 == arguments.length) return this._timeout; 3579 | if ('string' == typeof ms) ms = milliseconds(ms); 3580 | debug('timeout %d', ms); 3581 | this._timeout = ms; 3582 | if (this.timer) this.resetTimeout(); 3583 | return this; 3584 | }; 3585 | 3586 | /** 3587 | * Set & get slow `ms`. 3588 | * 3589 | * @param {Number|String} ms 3590 | * @return {Runnable|Number} ms or self 3591 | * @api private 3592 | */ 3593 | 3594 | Runnable.prototype.slow = function(ms){ 3595 | if (0 === arguments.length) return this._slow; 3596 | if ('string' == typeof ms) ms = milliseconds(ms); 3597 | debug('timeout %d', ms); 3598 | this._slow = ms; 3599 | return this; 3600 | }; 3601 | 3602 | /** 3603 | * Return the full title generated by recursively 3604 | * concatenating the parent's full title. 3605 | * 3606 | * @return {String} 3607 | * @api public 3608 | */ 3609 | 3610 | Runnable.prototype.fullTitle = function(){ 3611 | return this.parent.fullTitle() + ' ' + this.title; 3612 | }; 3613 | 3614 | /** 3615 | * Clear the timeout. 3616 | * 3617 | * @api private 3618 | */ 3619 | 3620 | Runnable.prototype.clearTimeout = function(){ 3621 | clearTimeout(this.timer); 3622 | }; 3623 | 3624 | /** 3625 | * Inspect the runnable void of private properties. 3626 | * 3627 | * @return {String} 3628 | * @api private 3629 | */ 3630 | 3631 | Runnable.prototype.inspect = function(){ 3632 | return JSON.stringify(this, function(key, val){ 3633 | if ('_' == key[0]) return; 3634 | if ('parent' == key) return '#'; 3635 | if ('ctx' == key) return '#'; 3636 | return val; 3637 | }, 2); 3638 | }; 3639 | 3640 | /** 3641 | * Reset the timeout. 3642 | * 3643 | * @api private 3644 | */ 3645 | 3646 | Runnable.prototype.resetTimeout = function(){ 3647 | var self = this 3648 | , ms = this.timeout(); 3649 | 3650 | this.clearTimeout(); 3651 | if (ms) { 3652 | this.timer = setTimeout(function(){ 3653 | self.callback(new Error('timeout of ' + ms + 'ms exceeded')); 3654 | self.timedOut = true; 3655 | }, ms); 3656 | } 3657 | }; 3658 | 3659 | /** 3660 | * Run the test and invoke `fn(err)`. 3661 | * 3662 | * @param {Function} fn 3663 | * @api private 3664 | */ 3665 | 3666 | Runnable.prototype.run = function(fn){ 3667 | var self = this 3668 | , ms = this.timeout() 3669 | , start = new Date 3670 | , ctx = this.ctx 3671 | , finished 3672 | , emitted; 3673 | 3674 | if (ctx) ctx.runnable(this); 3675 | 3676 | // timeout 3677 | if (this.async) { 3678 | if (ms) { 3679 | this.timer = setTimeout(function(){ 3680 | done(new Error('timeout of ' + ms + 'ms exceeded')); 3681 | self.timedOut = true; 3682 | }, ms); 3683 | } 3684 | } 3685 | 3686 | // called multiple times 3687 | function multiple(err) { 3688 | if (emitted) return; 3689 | emitted = true; 3690 | self.emit('error', err || new Error('done() called multiple times')); 3691 | } 3692 | 3693 | // finished 3694 | function done(err) { 3695 | if (self.timedOut) return; 3696 | if (finished) return multiple(err); 3697 | self.clearTimeout(); 3698 | self.duration = new Date - start; 3699 | finished = true; 3700 | fn(err); 3701 | } 3702 | 3703 | // for .resetTimeout() 3704 | this.callback = done; 3705 | 3706 | // async 3707 | if (this.async) { 3708 | try { 3709 | this.fn.call(ctx, function(err){ 3710 | if (toString.call(err) === "[object Error]") return done(err); 3711 | if (null != err) return done(new Error('done() invoked with non-Error: ' + err)); 3712 | done(); 3713 | }); 3714 | } catch (err) { 3715 | done(err); 3716 | } 3717 | return; 3718 | } 3719 | 3720 | if (this.asyncOnly) { 3721 | return done(new Error('--async-only option in use without declaring `done()`')); 3722 | } 3723 | 3724 | // sync 3725 | try { 3726 | if (!this.pending) this.fn.call(ctx); 3727 | this.duration = new Date - start; 3728 | fn(); 3729 | } catch (err) { 3730 | fn(err); 3731 | } 3732 | }; 3733 | 3734 | }); // module: runnable.js 3735 | 3736 | require.register("runner.js", function(module, exports, require){ 3737 | 3738 | /** 3739 | * Module dependencies. 3740 | */ 3741 | 3742 | var EventEmitter = require('browser/events').EventEmitter 3743 | , debug = require('browser/debug')('mocha:runner') 3744 | , Test = require('./test') 3745 | , utils = require('./utils') 3746 | , filter = utils.filter 3747 | , keys = utils.keys 3748 | , noop = function(){}; 3749 | 3750 | /** 3751 | * Non-enumerable globals. 3752 | */ 3753 | 3754 | var globals = [ 3755 | 'setTimeout', 3756 | 'clearTimeout', 3757 | 'setInterval', 3758 | 'clearInterval', 3759 | 'XMLHttpRequest', 3760 | 'Date' 3761 | ]; 3762 | 3763 | /** 3764 | * Expose `Runner`. 3765 | */ 3766 | 3767 | module.exports = Runner; 3768 | 3769 | /** 3770 | * Initialize a `Runner` for the given `suite`. 3771 | * 3772 | * Events: 3773 | * 3774 | * - `start` execution started 3775 | * - `end` execution complete 3776 | * - `suite` (suite) test suite execution started 3777 | * - `suite end` (suite) all tests (and sub-suites) have finished 3778 | * - `test` (test) test execution started 3779 | * - `test end` (test) test completed 3780 | * - `hook` (hook) hook execution started 3781 | * - `hook end` (hook) hook complete 3782 | * - `pass` (test) test passed 3783 | * - `fail` (test, err) test failed 3784 | * 3785 | * @api public 3786 | */ 3787 | 3788 | function Runner(suite) { 3789 | var self = this; 3790 | this._globals = []; 3791 | this.suite = suite; 3792 | this.total = suite.total(); 3793 | this.failures = 0; 3794 | this.on('test end', function(test){ self.checkGlobals(test); }); 3795 | this.on('hook end', function(hook){ self.checkGlobals(hook); }); 3796 | this.grep(/.*/); 3797 | this.globals(this.globalProps().concat(['errno'])); 3798 | } 3799 | 3800 | /** 3801 | * Inherit from `EventEmitter.prototype`. 3802 | */ 3803 | 3804 | Runner.prototype = new EventEmitter; 3805 | Runner.prototype.constructor = Runner; 3806 | 3807 | 3808 | /** 3809 | * Run tests with full titles matching `re`. Updates runner.total 3810 | * with number of tests matched. 3811 | * 3812 | * @param {RegExp} re 3813 | * @param {Boolean} invert 3814 | * @return {Runner} for chaining 3815 | * @api public 3816 | */ 3817 | 3818 | Runner.prototype.grep = function(re, invert){ 3819 | debug('grep %s', re); 3820 | this._grep = re; 3821 | this._invert = invert; 3822 | this.total = this.grepTotal(this.suite); 3823 | return this; 3824 | }; 3825 | 3826 | /** 3827 | * Returns the number of tests matching the grep search for the 3828 | * given suite. 3829 | * 3830 | * @param {Suite} suite 3831 | * @return {Number} 3832 | * @api public 3833 | */ 3834 | 3835 | Runner.prototype.grepTotal = function(suite) { 3836 | var self = this; 3837 | var total = 0; 3838 | 3839 | suite.eachTest(function(test){ 3840 | var match = self._grep.test(test.fullTitle()); 3841 | if (self._invert) match = !match; 3842 | if (match) total++; 3843 | }); 3844 | 3845 | return total; 3846 | }; 3847 | 3848 | /** 3849 | * Return a list of global properties. 3850 | * 3851 | * @return {Array} 3852 | * @api private 3853 | */ 3854 | 3855 | Runner.prototype.globalProps = function() { 3856 | var props = utils.keys(global); 3857 | 3858 | // non-enumerables 3859 | for (var i = 0; i < globals.length; ++i) { 3860 | if (~utils.indexOf(props, globals[i])) continue; 3861 | props.push(globals[i]); 3862 | } 3863 | 3864 | return props; 3865 | }; 3866 | 3867 | /** 3868 | * Allow the given `arr` of globals. 3869 | * 3870 | * @param {Array} arr 3871 | * @return {Runner} for chaining 3872 | * @api public 3873 | */ 3874 | 3875 | Runner.prototype.globals = function(arr){ 3876 | if (0 == arguments.length) return this._globals; 3877 | debug('globals %j', arr); 3878 | utils.forEach(arr, function(arr){ 3879 | this._globals.push(arr); 3880 | }, this); 3881 | return this; 3882 | }; 3883 | 3884 | /** 3885 | * Check for global variable leaks. 3886 | * 3887 | * @api private 3888 | */ 3889 | 3890 | Runner.prototype.checkGlobals = function(test){ 3891 | if (this.ignoreLeaks) return; 3892 | var ok = this._globals; 3893 | var globals = this.globalProps(); 3894 | var isNode = process.kill; 3895 | var leaks; 3896 | 3897 | // check length - 2 ('errno' and 'location' globals) 3898 | if (isNode && 1 == ok.length - globals.length) return 3899 | else if (2 == ok.length - globals.length) return; 3900 | 3901 | leaks = filterLeaks(ok, globals); 3902 | this._globals = this._globals.concat(leaks); 3903 | 3904 | if (leaks.length > 1) { 3905 | this.fail(test, new Error('global leaks detected: ' + leaks.join(', ') + '')); 3906 | } else if (leaks.length) { 3907 | this.fail(test, new Error('global leak detected: ' + leaks[0])); 3908 | } 3909 | }; 3910 | 3911 | /** 3912 | * Fail the given `test`. 3913 | * 3914 | * @param {Test} test 3915 | * @param {Error} err 3916 | * @api private 3917 | */ 3918 | 3919 | Runner.prototype.fail = function(test, err){ 3920 | ++this.failures; 3921 | test.state = 'failed'; 3922 | 3923 | if ('string' == typeof err) { 3924 | err = new Error('the string "' + err + '" was thrown, throw an Error :)'); 3925 | } 3926 | 3927 | this.emit('fail', test, err); 3928 | }; 3929 | 3930 | /** 3931 | * Fail the given `hook` with `err`. 3932 | * 3933 | * Hook failures (currently) hard-end due 3934 | * to that fact that a failing hook will 3935 | * surely cause subsequent tests to fail, 3936 | * causing jumbled reporting. 3937 | * 3938 | * @param {Hook} hook 3939 | * @param {Error} err 3940 | * @api private 3941 | */ 3942 | 3943 | Runner.prototype.failHook = function(hook, err){ 3944 | this.fail(hook, err); 3945 | this.emit('end'); 3946 | }; 3947 | 3948 | /** 3949 | * Run hook `name` callbacks and then invoke `fn()`. 3950 | * 3951 | * @param {String} name 3952 | * @param {Function} function 3953 | * @api private 3954 | */ 3955 | 3956 | Runner.prototype.hook = function(name, fn){ 3957 | var suite = this.suite 3958 | , hooks = suite['_' + name] 3959 | , self = this 3960 | , timer; 3961 | 3962 | function next(i) { 3963 | var hook = hooks[i]; 3964 | if (!hook) return fn(); 3965 | self.currentRunnable = hook; 3966 | 3967 | self.emit('hook', hook); 3968 | 3969 | hook.on('error', function(err){ 3970 | self.failHook(hook, err); 3971 | }); 3972 | 3973 | hook.run(function(err){ 3974 | hook.removeAllListeners('error'); 3975 | var testError = hook.error(); 3976 | if (testError) self.fail(self.test, testError); 3977 | if (err) return self.failHook(hook, err); 3978 | self.emit('hook end', hook); 3979 | next(++i); 3980 | }); 3981 | } 3982 | 3983 | process.nextTick(function(){ 3984 | next(0); 3985 | }); 3986 | }; 3987 | 3988 | /** 3989 | * Run hook `name` for the given array of `suites` 3990 | * in order, and callback `fn(err)`. 3991 | * 3992 | * @param {String} name 3993 | * @param {Array} suites 3994 | * @param {Function} fn 3995 | * @api private 3996 | */ 3997 | 3998 | Runner.prototype.hooks = function(name, suites, fn){ 3999 | var self = this 4000 | , orig = this.suite; 4001 | 4002 | function next(suite) { 4003 | self.suite = suite; 4004 | 4005 | if (!suite) { 4006 | self.suite = orig; 4007 | return fn(); 4008 | } 4009 | 4010 | self.hook(name, function(err){ 4011 | if (err) { 4012 | self.suite = orig; 4013 | return fn(err); 4014 | } 4015 | 4016 | next(suites.pop()); 4017 | }); 4018 | } 4019 | 4020 | next(suites.pop()); 4021 | }; 4022 | 4023 | /** 4024 | * Run hooks from the top level down. 4025 | * 4026 | * @param {String} name 4027 | * @param {Function} fn 4028 | * @api private 4029 | */ 4030 | 4031 | Runner.prototype.hookUp = function(name, fn){ 4032 | var suites = [this.suite].concat(this.parents()).reverse(); 4033 | this.hooks(name, suites, fn); 4034 | }; 4035 | 4036 | /** 4037 | * Run hooks from the bottom up. 4038 | * 4039 | * @param {String} name 4040 | * @param {Function} fn 4041 | * @api private 4042 | */ 4043 | 4044 | Runner.prototype.hookDown = function(name, fn){ 4045 | var suites = [this.suite].concat(this.parents()); 4046 | this.hooks(name, suites, fn); 4047 | }; 4048 | 4049 | /** 4050 | * Return an array of parent Suites from 4051 | * closest to furthest. 4052 | * 4053 | * @return {Array} 4054 | * @api private 4055 | */ 4056 | 4057 | Runner.prototype.parents = function(){ 4058 | var suite = this.suite 4059 | , suites = []; 4060 | while (suite = suite.parent) suites.push(suite); 4061 | return suites; 4062 | }; 4063 | 4064 | /** 4065 | * Run the current test and callback `fn(err)`. 4066 | * 4067 | * @param {Function} fn 4068 | * @api private 4069 | */ 4070 | 4071 | Runner.prototype.runTest = function(fn){ 4072 | var test = this.test 4073 | , self = this; 4074 | 4075 | if (this.asyncOnly) test.asyncOnly = true; 4076 | 4077 | try { 4078 | test.on('error', function(err){ 4079 | self.fail(test, err); 4080 | }); 4081 | test.run(fn); 4082 | } catch (err) { 4083 | fn(err); 4084 | } 4085 | }; 4086 | 4087 | /** 4088 | * Run tests in the given `suite` and invoke 4089 | * the callback `fn()` when complete. 4090 | * 4091 | * @param {Suite} suite 4092 | * @param {Function} fn 4093 | * @api private 4094 | */ 4095 | 4096 | Runner.prototype.runTests = function(suite, fn){ 4097 | var self = this 4098 | , tests = suite.tests.slice() 4099 | , test; 4100 | 4101 | function next(err) { 4102 | // if we bail after first err 4103 | if (self.failures && suite._bail) return fn(); 4104 | 4105 | // next test 4106 | test = tests.shift(); 4107 | 4108 | // all done 4109 | if (!test) return fn(); 4110 | 4111 | // grep 4112 | var match = self._grep.test(test.fullTitle()); 4113 | if (self._invert) match = !match; 4114 | if (!match) return next(); 4115 | 4116 | // pending 4117 | if (test.pending) { 4118 | self.emit('pending', test); 4119 | self.emit('test end', test); 4120 | return next(); 4121 | } 4122 | 4123 | // execute test and hook(s) 4124 | self.emit('test', self.test = test); 4125 | self.hookDown('beforeEach', function(){ 4126 | self.currentRunnable = self.test; 4127 | self.runTest(function(err){ 4128 | test = self.test; 4129 | 4130 | if (err) { 4131 | self.fail(test, err); 4132 | self.emit('test end', test); 4133 | return self.hookUp('afterEach', next); 4134 | } 4135 | 4136 | test.state = 'passed'; 4137 | self.emit('pass', test); 4138 | self.emit('test end', test); 4139 | self.hookUp('afterEach', next); 4140 | }); 4141 | }); 4142 | } 4143 | 4144 | this.next = next; 4145 | next(); 4146 | }; 4147 | 4148 | /** 4149 | * Run the given `suite` and invoke the 4150 | * callback `fn()` when complete. 4151 | * 4152 | * @param {Suite} suite 4153 | * @param {Function} fn 4154 | * @api private 4155 | */ 4156 | 4157 | Runner.prototype.runSuite = function(suite, fn){ 4158 | var total = this.grepTotal(suite) 4159 | , self = this 4160 | , i = 0; 4161 | 4162 | debug('run suite %s', suite.fullTitle()); 4163 | 4164 | if (!total) return fn(); 4165 | 4166 | this.emit('suite', this.suite = suite); 4167 | 4168 | function next() { 4169 | var curr = suite.suites[i++]; 4170 | if (!curr) return done(); 4171 | self.runSuite(curr, next); 4172 | } 4173 | 4174 | function done() { 4175 | self.suite = suite; 4176 | self.hook('afterAll', function(){ 4177 | self.emit('suite end', suite); 4178 | fn(); 4179 | }); 4180 | } 4181 | 4182 | this.hook('beforeAll', function(){ 4183 | self.runTests(suite, next); 4184 | }); 4185 | }; 4186 | 4187 | /** 4188 | * Handle uncaught exceptions. 4189 | * 4190 | * @param {Error} err 4191 | * @api private 4192 | */ 4193 | 4194 | Runner.prototype.uncaught = function(err){ 4195 | debug('uncaught exception %s', err.message); 4196 | var runnable = this.currentRunnable; 4197 | if (!runnable || 'failed' == runnable.state) return; 4198 | runnable.clearTimeout(); 4199 | err.uncaught = true; 4200 | this.fail(runnable, err); 4201 | 4202 | // recover from test 4203 | if ('test' == runnable.type) { 4204 | this.emit('test end', runnable); 4205 | this.hookUp('afterEach', this.next); 4206 | return; 4207 | } 4208 | 4209 | // bail on hooks 4210 | this.emit('end'); 4211 | }; 4212 | 4213 | /** 4214 | * Run the root suite and invoke `fn(failures)` 4215 | * on completion. 4216 | * 4217 | * @param {Function} fn 4218 | * @return {Runner} for chaining 4219 | * @api public 4220 | */ 4221 | 4222 | Runner.prototype.run = function(fn){ 4223 | var self = this 4224 | , fn = fn || function(){}; 4225 | 4226 | debug('start'); 4227 | 4228 | // callback 4229 | this.on('end', function(){ 4230 | debug('end'); 4231 | process.removeListener('uncaughtException', function(err){ 4232 | self.uncaught(err); 4233 | }); 4234 | fn(self.failures); 4235 | }); 4236 | 4237 | // run suites 4238 | this.emit('start'); 4239 | this.runSuite(this.suite, function(){ 4240 | debug('finished running'); 4241 | self.emit('end'); 4242 | }); 4243 | 4244 | // uncaught exception 4245 | process.on('uncaughtException', function(err){ 4246 | self.uncaught(err); 4247 | }); 4248 | 4249 | return this; 4250 | }; 4251 | 4252 | /** 4253 | * Filter leaks with the given globals flagged as `ok`. 4254 | * 4255 | * @param {Array} ok 4256 | * @param {Array} globals 4257 | * @return {Array} 4258 | * @api private 4259 | */ 4260 | 4261 | function filterLeaks(ok, globals) { 4262 | return filter(globals, function(key){ 4263 | var matched = filter(ok, function(ok){ 4264 | if (~ok.indexOf('*')) return 0 == key.indexOf(ok.split('*')[0]); 4265 | // Opera and IE expose global variables for HTML element IDs (issue #243) 4266 | if (/^mocha-/.test(key)) return true; 4267 | return key == ok; 4268 | }); 4269 | return matched.length == 0 && (!global.navigator || 'onerror' !== key); 4270 | }); 4271 | } 4272 | 4273 | }); // module: runner.js 4274 | 4275 | require.register("suite.js", function(module, exports, require){ 4276 | 4277 | /** 4278 | * Module dependencies. 4279 | */ 4280 | 4281 | var EventEmitter = require('browser/events').EventEmitter 4282 | , debug = require('browser/debug')('mocha:suite') 4283 | , milliseconds = require('./ms') 4284 | , utils = require('./utils') 4285 | , Hook = require('./hook'); 4286 | 4287 | /** 4288 | * Expose `Suite`. 4289 | */ 4290 | 4291 | exports = module.exports = Suite; 4292 | 4293 | /** 4294 | * Create a new `Suite` with the given `title` 4295 | * and parent `Suite`. When a suite with the 4296 | * same title is already present, that suite 4297 | * is returned to provide nicer reporter 4298 | * and more flexible meta-testing. 4299 | * 4300 | * @param {Suite} parent 4301 | * @param {String} title 4302 | * @return {Suite} 4303 | * @api public 4304 | */ 4305 | 4306 | exports.create = function(parent, title){ 4307 | var suite = new Suite(title, parent.ctx); 4308 | suite.parent = parent; 4309 | if (parent.pending) suite.pending = true; 4310 | title = suite.fullTitle(); 4311 | parent.addSuite(suite); 4312 | return suite; 4313 | }; 4314 | 4315 | /** 4316 | * Initialize a new `Suite` with the given 4317 | * `title` and `ctx`. 4318 | * 4319 | * @param {String} title 4320 | * @param {Context} ctx 4321 | * @api private 4322 | */ 4323 | 4324 | function Suite(title, ctx) { 4325 | this.title = title; 4326 | this.ctx = ctx; 4327 | this.suites = []; 4328 | this.tests = []; 4329 | this.pending = false; 4330 | this._beforeEach = []; 4331 | this._beforeAll = []; 4332 | this._afterEach = []; 4333 | this._afterAll = []; 4334 | this.root = !title; 4335 | this._timeout = 2000; 4336 | this._slow = 75; 4337 | this._bail = false; 4338 | } 4339 | 4340 | /** 4341 | * Inherit from `EventEmitter.prototype`. 4342 | */ 4343 | 4344 | Suite.prototype = new EventEmitter; 4345 | Suite.prototype.constructor = Suite; 4346 | 4347 | 4348 | /** 4349 | * Return a clone of this `Suite`. 4350 | * 4351 | * @return {Suite} 4352 | * @api private 4353 | */ 4354 | 4355 | Suite.prototype.clone = function(){ 4356 | var suite = new Suite(this.title); 4357 | debug('clone'); 4358 | suite.ctx = this.ctx; 4359 | suite.timeout(this.timeout()); 4360 | suite.slow(this.slow()); 4361 | suite.bail(this.bail()); 4362 | return suite; 4363 | }; 4364 | 4365 | /** 4366 | * Set timeout `ms` or short-hand such as "2s". 4367 | * 4368 | * @param {Number|String} ms 4369 | * @return {Suite|Number} for chaining 4370 | * @api private 4371 | */ 4372 | 4373 | Suite.prototype.timeout = function(ms){ 4374 | if (0 == arguments.length) return this._timeout; 4375 | if ('string' == typeof ms) ms = milliseconds(ms); 4376 | debug('timeout %d', ms); 4377 | this._timeout = parseInt(ms, 10); 4378 | return this; 4379 | }; 4380 | 4381 | /** 4382 | * Set slow `ms` or short-hand such as "2s". 4383 | * 4384 | * @param {Number|String} ms 4385 | * @return {Suite|Number} for chaining 4386 | * @api private 4387 | */ 4388 | 4389 | Suite.prototype.slow = function(ms){ 4390 | if (0 === arguments.length) return this._slow; 4391 | if ('string' == typeof ms) ms = milliseconds(ms); 4392 | debug('slow %d', ms); 4393 | this._slow = ms; 4394 | return this; 4395 | }; 4396 | 4397 | /** 4398 | * Sets whether to bail after first error. 4399 | * 4400 | * @parma {Boolean} bail 4401 | * @return {Suite|Number} for chaining 4402 | * @api private 4403 | */ 4404 | 4405 | Suite.prototype.bail = function(bail){ 4406 | if (0 == arguments.length) return this._bail; 4407 | debug('bail %s', bail); 4408 | this._bail = bail; 4409 | return this; 4410 | }; 4411 | 4412 | /** 4413 | * Run `fn(test[, done])` before running tests. 4414 | * 4415 | * @param {Function} fn 4416 | * @return {Suite} for chaining 4417 | * @api private 4418 | */ 4419 | 4420 | Suite.prototype.beforeAll = function(fn){ 4421 | if (this.pending) return this; 4422 | var hook = new Hook('"before all" hook', fn); 4423 | hook.parent = this; 4424 | hook.timeout(this.timeout()); 4425 | hook.slow(this.slow()); 4426 | hook.ctx = this.ctx; 4427 | this._beforeAll.push(hook); 4428 | this.emit('beforeAll', hook); 4429 | return this; 4430 | }; 4431 | 4432 | /** 4433 | * Run `fn(test[, done])` after running tests. 4434 | * 4435 | * @param {Function} fn 4436 | * @return {Suite} for chaining 4437 | * @api private 4438 | */ 4439 | 4440 | Suite.prototype.afterAll = function(fn){ 4441 | if (this.pending) return this; 4442 | var hook = new Hook('"after all" hook', fn); 4443 | hook.parent = this; 4444 | hook.timeout(this.timeout()); 4445 | hook.slow(this.slow()); 4446 | hook.ctx = this.ctx; 4447 | this._afterAll.push(hook); 4448 | this.emit('afterAll', hook); 4449 | return this; 4450 | }; 4451 | 4452 | /** 4453 | * Run `fn(test[, done])` before each test case. 4454 | * 4455 | * @param {Function} fn 4456 | * @return {Suite} for chaining 4457 | * @api private 4458 | */ 4459 | 4460 | Suite.prototype.beforeEach = function(fn){ 4461 | if (this.pending) return this; 4462 | var hook = new Hook('"before each" hook', fn); 4463 | hook.parent = this; 4464 | hook.timeout(this.timeout()); 4465 | hook.slow(this.slow()); 4466 | hook.ctx = this.ctx; 4467 | this._beforeEach.push(hook); 4468 | this.emit('beforeEach', hook); 4469 | return this; 4470 | }; 4471 | 4472 | /** 4473 | * Run `fn(test[, done])` after each test case. 4474 | * 4475 | * @param {Function} fn 4476 | * @return {Suite} for chaining 4477 | * @api private 4478 | */ 4479 | 4480 | Suite.prototype.afterEach = function(fn){ 4481 | if (this.pending) return this; 4482 | var hook = new Hook('"after each" hook', fn); 4483 | hook.parent = this; 4484 | hook.timeout(this.timeout()); 4485 | hook.slow(this.slow()); 4486 | hook.ctx = this.ctx; 4487 | this._afterEach.push(hook); 4488 | this.emit('afterEach', hook); 4489 | return this; 4490 | }; 4491 | 4492 | /** 4493 | * Add a test `suite`. 4494 | * 4495 | * @param {Suite} suite 4496 | * @return {Suite} for chaining 4497 | * @api private 4498 | */ 4499 | 4500 | Suite.prototype.addSuite = function(suite){ 4501 | suite.parent = this; 4502 | suite.timeout(this.timeout()); 4503 | suite.slow(this.slow()); 4504 | suite.bail(this.bail()); 4505 | this.suites.push(suite); 4506 | this.emit('suite', suite); 4507 | return this; 4508 | }; 4509 | 4510 | /** 4511 | * Add a `test` to this suite. 4512 | * 4513 | * @param {Test} test 4514 | * @return {Suite} for chaining 4515 | * @api private 4516 | */ 4517 | 4518 | Suite.prototype.addTest = function(test){ 4519 | test.parent = this; 4520 | test.timeout(this.timeout()); 4521 | test.slow(this.slow()); 4522 | test.ctx = this.ctx; 4523 | this.tests.push(test); 4524 | this.emit('test', test); 4525 | return this; 4526 | }; 4527 | 4528 | /** 4529 | * Return the full title generated by recursively 4530 | * concatenating the parent's full title. 4531 | * 4532 | * @return {String} 4533 | * @api public 4534 | */ 4535 | 4536 | Suite.prototype.fullTitle = function(){ 4537 | if (this.parent) { 4538 | var full = this.parent.fullTitle(); 4539 | if (full) return full + ' ' + this.title; 4540 | } 4541 | return this.title; 4542 | }; 4543 | 4544 | /** 4545 | * Return the total number of tests. 4546 | * 4547 | * @return {Number} 4548 | * @api public 4549 | */ 4550 | 4551 | Suite.prototype.total = function(){ 4552 | return utils.reduce(this.suites, function(sum, suite){ 4553 | return sum + suite.total(); 4554 | }, 0) + this.tests.length; 4555 | }; 4556 | 4557 | /** 4558 | * Iterates through each suite recursively to find 4559 | * all tests. Applies a function in the format 4560 | * `fn(test)`. 4561 | * 4562 | * @param {Function} fn 4563 | * @return {Suite} 4564 | * @api private 4565 | */ 4566 | 4567 | Suite.prototype.eachTest = function(fn){ 4568 | utils.forEach(this.tests, fn); 4569 | utils.forEach(this.suites, function(suite){ 4570 | suite.eachTest(fn); 4571 | }); 4572 | return this; 4573 | }; 4574 | 4575 | }); // module: suite.js 4576 | 4577 | require.register("test.js", function(module, exports, require){ 4578 | 4579 | /** 4580 | * Module dependencies. 4581 | */ 4582 | 4583 | var Runnable = require('./runnable'); 4584 | 4585 | /** 4586 | * Expose `Test`. 4587 | */ 4588 | 4589 | module.exports = Test; 4590 | 4591 | /** 4592 | * Initialize a new `Test` with the given `title` and callback `fn`. 4593 | * 4594 | * @param {String} title 4595 | * @param {Function} fn 4596 | * @api private 4597 | */ 4598 | 4599 | function Test(title, fn) { 4600 | Runnable.call(this, title, fn); 4601 | this.pending = !fn; 4602 | this.type = 'test'; 4603 | } 4604 | 4605 | /** 4606 | * Inherit from `Runnable.prototype`. 4607 | */ 4608 | 4609 | Test.prototype = new Runnable; 4610 | Test.prototype.constructor = Test; 4611 | 4612 | 4613 | }); // module: test.js 4614 | 4615 | require.register("utils.js", function(module, exports, require){ 4616 | 4617 | /** 4618 | * Module dependencies. 4619 | */ 4620 | 4621 | var fs = require('browser/fs') 4622 | , path = require('browser/path') 4623 | , join = path.join 4624 | , debug = require('browser/debug')('mocha:watch'); 4625 | 4626 | /** 4627 | * Ignored directories. 4628 | */ 4629 | 4630 | var ignore = ['node_modules', '.git']; 4631 | 4632 | /** 4633 | * Escape special characters in the given string of html. 4634 | * 4635 | * @param {String} html 4636 | * @return {String} 4637 | * @api private 4638 | */ 4639 | 4640 | exports.escape = function(html){ 4641 | return String(html) 4642 | .replace(/&/g, '&') 4643 | .replace(/"/g, '"') 4644 | .replace(//g, '>'); 4646 | }; 4647 | 4648 | /** 4649 | * Array#forEach (<=IE8) 4650 | * 4651 | * @param {Array} array 4652 | * @param {Function} fn 4653 | * @param {Object} scope 4654 | * @api private 4655 | */ 4656 | 4657 | exports.forEach = function(arr, fn, scope){ 4658 | for (var i = 0, l = arr.length; i < l; i++) 4659 | fn.call(scope, arr[i], i); 4660 | }; 4661 | 4662 | /** 4663 | * Array#indexOf (<=IE8) 4664 | * 4665 | * @parma {Array} arr 4666 | * @param {Object} obj to find index of 4667 | * @param {Number} start 4668 | * @api private 4669 | */ 4670 | 4671 | exports.indexOf = function(arr, obj, start){ 4672 | for (var i = start || 0, l = arr.length; i < l; i++) { 4673 | if (arr[i] === obj) 4674 | return i; 4675 | } 4676 | return -1; 4677 | }; 4678 | 4679 | /** 4680 | * Array#reduce (<=IE8) 4681 | * 4682 | * @param {Array} array 4683 | * @param {Function} fn 4684 | * @param {Object} initial value 4685 | * @api private 4686 | */ 4687 | 4688 | exports.reduce = function(arr, fn, val){ 4689 | var rval = val; 4690 | 4691 | for (var i = 0, l = arr.length; i < l; i++) { 4692 | rval = fn(rval, arr[i], i, arr); 4693 | } 4694 | 4695 | return rval; 4696 | }; 4697 | 4698 | /** 4699 | * Array#filter (<=IE8) 4700 | * 4701 | * @param {Array} array 4702 | * @param {Function} fn 4703 | * @api private 4704 | */ 4705 | 4706 | exports.filter = function(arr, fn){ 4707 | var ret = []; 4708 | 4709 | for (var i = 0, l = arr.length; i < l; i++) { 4710 | var val = arr[i]; 4711 | if (fn(val, i, arr)) ret.push(val); 4712 | } 4713 | 4714 | return ret; 4715 | }; 4716 | 4717 | /** 4718 | * Object.keys (<=IE8) 4719 | * 4720 | * @param {Object} obj 4721 | * @return {Array} keys 4722 | * @api private 4723 | */ 4724 | 4725 | exports.keys = Object.keys || function(obj) { 4726 | var keys = [] 4727 | , has = Object.prototype.hasOwnProperty // for `window` on <=IE8 4728 | 4729 | for (var key in obj) { 4730 | if (has.call(obj, key)) { 4731 | keys.push(key); 4732 | } 4733 | } 4734 | 4735 | return keys; 4736 | }; 4737 | 4738 | /** 4739 | * Watch the given `files` for changes 4740 | * and invoke `fn(file)` on modification. 4741 | * 4742 | * @param {Array} files 4743 | * @param {Function} fn 4744 | * @api private 4745 | */ 4746 | 4747 | exports.watch = function(files, fn){ 4748 | var options = { interval: 100 }; 4749 | files.forEach(function(file){ 4750 | debug('file %s', file); 4751 | fs.watchFile(file, options, function(curr, prev){ 4752 | if (prev.mtime < curr.mtime) fn(file); 4753 | }); 4754 | }); 4755 | }; 4756 | 4757 | /** 4758 | * Ignored files. 4759 | */ 4760 | 4761 | function ignored(path){ 4762 | return !~ignore.indexOf(path); 4763 | } 4764 | 4765 | /** 4766 | * Lookup files in the given `dir`. 4767 | * 4768 | * @return {Array} 4769 | * @api private 4770 | */ 4771 | 4772 | exports.files = function(dir, ret){ 4773 | ret = ret || []; 4774 | 4775 | fs.readdirSync(dir) 4776 | .filter(ignored) 4777 | .forEach(function(path){ 4778 | path = join(dir, path); 4779 | if (fs.statSync(path).isDirectory()) { 4780 | exports.files(path, ret); 4781 | } else if (path.match(/\.(js|coffee)$/)) { 4782 | ret.push(path); 4783 | } 4784 | }); 4785 | 4786 | return ret; 4787 | }; 4788 | 4789 | /** 4790 | * Compute a slug from the given `str`. 4791 | * 4792 | * @param {String} str 4793 | * @return {String} 4794 | * @api private 4795 | */ 4796 | 4797 | exports.slug = function(str){ 4798 | return str 4799 | .toLowerCase() 4800 | .replace(/ +/g, '-') 4801 | .replace(/[^-\w]/g, ''); 4802 | }; 4803 | 4804 | /** 4805 | * Strip the function definition from `str`, 4806 | * and re-indent for pre whitespace. 4807 | */ 4808 | 4809 | exports.clean = function(str) { 4810 | str = str 4811 | .replace(/^function *\(.*\) *{/, '') 4812 | .replace(/\s+\}$/, ''); 4813 | 4814 | var spaces = str.match(/^\n?( *)/)[1].length 4815 | , re = new RegExp('^ {' + spaces + '}', 'gm'); 4816 | 4817 | str = str.replace(re, ''); 4818 | 4819 | return exports.trim(str); 4820 | }; 4821 | 4822 | /** 4823 | * Escape regular expression characters in `str`. 4824 | * 4825 | * @param {String} str 4826 | * @return {String} 4827 | * @api private 4828 | */ 4829 | 4830 | exports.escapeRegexp = function(str){ 4831 | return str.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&"); 4832 | }; 4833 | 4834 | /** 4835 | * Trim the given `str`. 4836 | * 4837 | * @param {String} str 4838 | * @return {String} 4839 | * @api private 4840 | */ 4841 | 4842 | exports.trim = function(str){ 4843 | return str.replace(/^\s+|\s+$/g, ''); 4844 | }; 4845 | 4846 | /** 4847 | * Parse the given `qs`. 4848 | * 4849 | * @param {String} qs 4850 | * @return {Object} 4851 | * @api private 4852 | */ 4853 | 4854 | exports.parseQuery = function(qs){ 4855 | return exports.reduce(qs.replace('?', '').split('&'), function(obj, pair){ 4856 | var i = pair.indexOf('=') 4857 | , key = pair.slice(0, i) 4858 | , val = pair.slice(++i); 4859 | 4860 | obj[key] = decodeURIComponent(val); 4861 | return obj; 4862 | }, {}); 4863 | }; 4864 | 4865 | /** 4866 | * Highlight the given string of `js`. 4867 | * 4868 | * @param {String} js 4869 | * @return {String} 4870 | * @api private 4871 | */ 4872 | 4873 | function highlight(js) { 4874 | return js 4875 | .replace(//g, '>') 4877 | .replace(/\/\/(.*)/gm, '//$1') 4878 | .replace(/('.*?')/gm, '$1') 4879 | .replace(/(\d+\.\d+)/gm, '$1') 4880 | .replace(/(\d+)/gm, '$1') 4881 | .replace(/\bnew *(\w+)/gm, 'new $1') 4882 | .replace(/\b(function|new|throw|return|var|if|else)\b/gm, '$1') 4883 | } 4884 | 4885 | /** 4886 | * Highlight the contents of tag `name`. 4887 | * 4888 | * @param {String} name 4889 | * @api private 4890 | */ 4891 | 4892 | exports.highlightTags = function(name) { 4893 | var code = document.getElementsByTagName(name); 4894 | for (var i = 0, len = code.length; i < len; ++i) { 4895 | code[i].innerHTML = highlight(code[i].innerHTML); 4896 | } 4897 | }; 4898 | 4899 | }); // module: utils.js 4900 | /** 4901 | * Node shims. 4902 | * 4903 | * These are meant only to allow 4904 | * mocha.js to run untouched, not 4905 | * to allow running node code in 4906 | * the browser. 4907 | */ 4908 | 4909 | process = {}; 4910 | process.exit = function(status){}; 4911 | process.stdout = {}; 4912 | global = window; 4913 | 4914 | /** 4915 | * next tick implementation. 4916 | */ 4917 | 4918 | process.nextTick = (function(){ 4919 | // postMessage behaves badly on IE8 4920 | if (window.ActiveXObject || !window.postMessage) { 4921 | return function(fn){ fn() }; 4922 | } 4923 | 4924 | // based on setZeroTimeout by David Baron 4925 | // - http://dbaron.org/log/20100309-faster-timeouts 4926 | var timeouts = [] 4927 | , name = 'mocha-zero-timeout' 4928 | 4929 | window.addEventListener('message', function(e){ 4930 | if (e.source == window && e.data == name) { 4931 | if (e.stopPropagation) e.stopPropagation(); 4932 | if (timeouts.length) timeouts.shift()(); 4933 | } 4934 | }, true); 4935 | 4936 | return function(fn){ 4937 | timeouts.push(fn); 4938 | window.postMessage(name, '*'); 4939 | } 4940 | })(); 4941 | 4942 | /** 4943 | * Remove uncaughtException listener. 4944 | */ 4945 | 4946 | process.removeListener = function(e){ 4947 | if ('uncaughtException' == e) { 4948 | window.onerror = null; 4949 | } 4950 | }; 4951 | 4952 | /** 4953 | * Implements uncaughtException listener. 4954 | */ 4955 | 4956 | process.on = function(e, fn){ 4957 | if ('uncaughtException' == e) { 4958 | window.onerror = function(err, url, line){ 4959 | fn(new Error(err + ' (' + url + ':' + line + ')')); 4960 | }; 4961 | } 4962 | }; 4963 | 4964 | // boot 4965 | ;(function(){ 4966 | 4967 | /** 4968 | * Expose mocha. 4969 | */ 4970 | 4971 | var Mocha = window.Mocha = require('mocha'), 4972 | mocha = window.mocha = new Mocha({ reporter: 'html' }); 4973 | 4974 | /** 4975 | * Override ui to ensure that the ui functions are initialized. 4976 | * Normally this would happen in Mocha.prototype.loadFiles. 4977 | */ 4978 | 4979 | mocha.ui = function(ui){ 4980 | Mocha.prototype.ui.call(this, ui); 4981 | this.suite.emit('pre-require', window, null, this); 4982 | return this; 4983 | }; 4984 | 4985 | /** 4986 | * Setup mocha with the given setting options. 4987 | */ 4988 | 4989 | mocha.setup = function(opts){ 4990 | if ('string' == typeof opts) opts = { ui: opts }; 4991 | for (var opt in opts) this[opt](opts[opt]); 4992 | return this; 4993 | }; 4994 | 4995 | /** 4996 | * Run mocha, returning the Runner. 4997 | */ 4998 | 4999 | mocha.run = function(fn){ 5000 | var options = mocha.options; 5001 | mocha.globals('location'); 5002 | 5003 | var query = Mocha.utils.parseQuery(window.location.search || ''); 5004 | if (query.grep) mocha.grep(query.grep); 5005 | if (query.invert) mocha.invert(); 5006 | 5007 | return Mocha.prototype.run.call(mocha, function(){ 5008 | Mocha.utils.highlightTags('code'); 5009 | if (fn) fn(); 5010 | }); 5011 | }; 5012 | })(); 5013 | })(); -------------------------------------------------------------------------------- /test/markdown.js: -------------------------------------------------------------------------------- 1 | var constructor = require('..'), 2 | assert = require('assert'); 3 | 4 | 5 | describe("Markdown", function() { 6 | 7 | describe("Basic", function() { 8 | var marc; 9 | beforeEach(function() { 10 | marc = constructor(); 11 | }); 12 | 13 | it('should render html', function() { 14 | console.time('basic'); 15 | var result = marc('I am using __markdown__.'); 16 | console.timeEnd('basic'); 17 | assert.equal(result,'I am using markdown.\n'); 18 | }); 19 | 20 | }); 21 | 22 | describe("Dynamic", function() { 23 | var marc; 24 | beforeEach(function() { 25 | marc = constructor({ 26 | label: 'markdown' 27 | }); 28 | }); 29 | 30 | it('should render html with data', function() { 31 | console.time('dynamic'); 32 | var result = marc('I am using __{{label}}__.', true); 33 | console.timeEnd('dynamic'); 34 | assert.equal(result,'I am using markdown.\n'); 35 | }); 36 | 37 | it('should update rendered html', function(done) { 38 | var expected = 'I am using other.\n'; 39 | var result = marc('I am using __{{label}}__.', function(val) { 40 | if(val === expected) done(); 41 | }); 42 | assert.equal(result, 'I am using markdown.\n'); 43 | marc.set('label', 'other'); 44 | }); 45 | 46 | it('should update html everytime data changes', function(done) { 47 | var i = 0, 48 | expected = 'I am using bredele.\n'; 49 | var result = marc('I am using __{{label}}__.', function(val) { 50 | i++; 51 | if(i === 2 && val === expected) done(); 52 | }); 53 | marc.set('label', 'other'); 54 | marc.set('label', 'bredele'); 55 | }); 56 | 57 | it('should filter markdown', function() { 58 | marc.filter('upper', function(str) { 59 | return str.toUpperCase(); 60 | }); 61 | var result = marc('I am using __{{label} | upper}__.', true); 62 | assert.equal(result,'I am using MARKDOWN.\n'); 63 | }); 64 | 65 | // console.log(marc('__{{>partial}}__.')); 66 | 67 | }); 68 | 69 | 70 | 71 | }); -------------------------------------------------------------------------------- /test/markdown/amy.html: -------------------------------------------------------------------------------- 1 | I am using markdown. 2 | -------------------------------------------------------------------------------- /test/markdown/basic.md: -------------------------------------------------------------------------------- 1 | I am using __markdown__. -------------------------------------------------------------------------------- /test/options.js: -------------------------------------------------------------------------------- 1 | var constructor = require('..'), 2 | assert = require('assert'); 3 | 4 | 5 | describe("Options", function() { 6 | 7 | var marc; 8 | beforeEach(function() { 9 | marc = constructor(); 10 | }); 11 | 12 | it("should have a options handler", function() { 13 | assert.equal(typeof marc.config, 'function'); 14 | }); 15 | 16 | 17 | it("should set and get option", function() { 18 | marc.config('sanitize', true); 19 | assert.equal(marc.config('sanitize'), true); 20 | }); 21 | 22 | it('should set multiple options', function() { 23 | marc.config({ 24 | sanitize: true, 25 | escaped:false 26 | }); 27 | assert.equal(marc.config('sanitize'), true); 28 | assert.equal(marc.config('escaped'), false); 29 | }); 30 | 31 | }); 32 | -------------------------------------------------------------------------------- /test/partials.js: -------------------------------------------------------------------------------- 1 | var constructor = require('..'), 2 | assert = require('assert'); 3 | 4 | 5 | describe("Partials", function() { 6 | var marc; 7 | beforeEach(function() { 8 | marc = constructor(); 9 | }); 10 | 11 | it("should add partials markdown", function() { 12 | assert.equal(typeof marc.partial, 'function'); 13 | }); 14 | 15 | describe("render:", function() { 16 | 17 | it("should render partials", function() { 18 | marc.partial('hello', '__hello world!__'); 19 | var result = marc('This is a partial {> hello }', true); 20 | assert.equal(result, 'This is a partial hello world!\n'); 21 | }); 22 | 23 | it('should render partials with data', function() { 24 | marc.partial('hello', '__{{label}}!__'); 25 | marc.set({ 26 | label: 'hello world', 27 | name: 'hello' 28 | }); 29 | var result = marc('This is the partial {{ name }}:{> hello }', true); 30 | assert.equal(result, 'This is the partial hello:hello world!\n'); 31 | }); 32 | 33 | it('should update partials on changes', function(done) { 34 | marc.partial('hello', '__{{label}}!__'); 35 | var result = marc('This is a partial {> hello }', function(val) { 36 | if(val === 'This is a partial hello world!\n') done(); 37 | }); 38 | marc.set('label', 'hello world'); 39 | }); 40 | 41 | it('should update partials on changes', function(done) { 42 | marc.partial('hello', '__{{name}}!__'); 43 | marc.set('name', 'hello world'); 44 | var result = marc('This is a {{label}} {> hello }', function(val) { 45 | if(val === 'This is a partial hello world!\n') done(); 46 | }); 47 | marc.set('label', 'partial'); 48 | }); 49 | 50 | }); 51 | 52 | 53 | }); 54 | --------------------------------------------------------------------------------
I am using other.
I am using bredele.
I am using MARKDOWN.
This is a partial hello world!
This is the partial hello:hello world!
%s
%e