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