');
100 | li.className = 'fail';
101 | }
102 | else {
103 | li.innerHTML = a.message || a.method || 'no message';
104 | li.className = 'pass';
105 | }
106 | aList.appendChild(li);
107 | }
108 | test.appendChild(aList);
109 | tests.appendChild(test);
110 | },
111 | done: function (assertions) {
112 | var end = new Date().getTime();
113 | var duration = end - start;
114 |
115 | var failures = assertions.failures();
116 | banner.className = failures ? 'fail': 'pass';
117 |
118 | result.innerHTML = 'Tests completed in ' + duration +
119 | ' milliseconds. ' +
120 | assertions.passes() + ' assertions of ' +
121 | '' + assertions.length + ' passed, ' +
122 | assertions.failures() + ' failed.';
123 |
124 | if (callback) callback(assertions.failures() ? new Error('We have got test failures.') : undefined);
125 | }
126 | });
127 | };
128 |
--------------------------------------------------------------------------------
/lib/types.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Nodeunit
3 | * Copyright (c) 2010 Caolan McMahon
4 | * MIT Licensed
5 | *
6 | * THIS FILE SHOULD BE BROWSER-COMPATIBLE JS!
7 | * You can use @REMOVE_LINE_FOR_BROWSER to remove code from the browser build.
8 | * Only code on that line will be removed, it's mostly to avoid requiring code
9 | * that is node specific
10 | */
11 |
12 | /**
13 | * Module dependencies
14 | */
15 |
16 | var assert = require('./assert'), //@REMOVE_LINE_FOR_BROWSER
17 | async = require('../deps/async'); //@REMOVE_LINE_FOR_BROWSER
18 |
19 |
20 | /**
21 | * Creates assertion objects representing the result of an assert call.
22 | * Accepts an object or AssertionError as its argument.
23 | *
24 | * @param {object} obj
25 | * @api public
26 | */
27 |
28 | exports.assertion = function (obj) {
29 | return {
30 | method: obj.method || '',
31 | message: obj.message || (obj.error && obj.error.message) || '',
32 | error: obj.error,
33 | passed: function () {
34 | return !this.error;
35 | },
36 | failed: function () {
37 | return Boolean(this.error);
38 | }
39 | };
40 | };
41 |
42 | /**
43 | * Creates an assertion list object representing a group of assertions.
44 | * Accepts an array of assertion objects.
45 | *
46 | * @param {Array} arr
47 | * @param {Number} duration
48 | * @api public
49 | */
50 |
51 | exports.assertionList = function (arr, duration) {
52 | var that = arr || [];
53 | that.failures = function () {
54 | var failures = 0;
55 | for (var i = 0; i < this.length; i += 1) {
56 | if (this[i].failed()) {
57 | failures += 1;
58 | }
59 | }
60 | return failures;
61 | };
62 | that.passes = function () {
63 | return that.length - that.failures();
64 | };
65 | that.duration = duration || 0;
66 | return that;
67 | };
68 |
69 | /**
70 | * Create a wrapper function for assert module methods. Executes a callback
71 | * after it's complete with an assertion object representing the result.
72 | *
73 | * @param {Function} callback
74 | * @api private
75 | */
76 |
77 | var assertWrapper = function (callback) {
78 | return function (new_method, assert_method, arity) {
79 | return function () {
80 | var message = arguments[arity - 1];
81 | var a = exports.assertion({method: new_method, message: message});
82 | try {
83 | assert[assert_method].apply(null, arguments);
84 | }
85 | catch (e) {
86 | a.error = e;
87 | }
88 | callback(a);
89 | };
90 | };
91 | };
92 |
93 | /**
94 | * Creates the 'test' object that gets passed to every test function.
95 | * Accepts the name of the test function as its first argument, followed by
96 | * the start time in ms, the options object and a callback function.
97 | *
98 | * @param {String} name
99 | * @param {Number} start
100 | * @param {Object} options
101 | * @param {Function} callback
102 | * @api public
103 | */
104 |
105 | exports.test = function (name, start, options, callback) {
106 | var expecting;
107 | var a_list = [];
108 |
109 | var wrapAssert = assertWrapper(function (a) {
110 | a_list.push(a);
111 | if (options.log) {
112 | async.nextTick(function () {
113 | options.log(a);
114 | });
115 | }
116 | });
117 |
118 | var test = {
119 | done: function (err) {
120 | if (expecting !== undefined && expecting !== a_list.length) {
121 | var e = new Error(
122 | 'Expected ' + expecting + ' assertions, ' +
123 | a_list.length + ' ran'
124 | );
125 | var a1 = exports.assertion({method: 'expect', error: e});
126 | a_list.push(a1);
127 | if (options.log) {
128 | async.nextTick(function () {
129 | options.log(a1);
130 | });
131 | }
132 | }
133 | if (err) {
134 | var a2 = exports.assertion({error: err});
135 | a_list.push(a2);
136 | if (options.log) {
137 | async.nextTick(function () {
138 | options.log(a2);
139 | });
140 | }
141 | }
142 | var end = new Date().getTime();
143 | async.nextTick(function () {
144 | var assertion_list = exports.assertionList(a_list, end - start);
145 | options.testDone(name, assertion_list);
146 | callback(null, a_list);
147 | });
148 | },
149 | ok: wrapAssert('ok', 'ok', 2),
150 | same: wrapAssert('same', 'deepEqual', 3),
151 | equals: wrapAssert('equals', 'equal', 3),
152 | expect: function (num) {
153 | expecting = num;
154 | },
155 | _assertion_list: a_list
156 | };
157 | // add all functions from the assert module
158 | for (var k in assert) {
159 | if (assert.hasOwnProperty(k)) {
160 | test[k] = wrapAssert(k, k, assert[k].length);
161 | }
162 | }
163 | return test;
164 | };
165 |
166 | /**
167 | * Ensures an options object has all callbacks, adding empty callback functions
168 | * if any are missing.
169 | *
170 | * @param {Object} opt
171 | * @return {Object}
172 | * @api public
173 | */
174 |
175 | exports.options = function (opt) {
176 | var optionalCallback = function (name) {
177 | opt[name] = opt[name] || function () {};
178 | };
179 |
180 | optionalCallback('moduleStart');
181 | optionalCallback('moduleDone');
182 | optionalCallback('testStart');
183 | optionalCallback('testReady');
184 | optionalCallback('testDone');
185 | //optionalCallback('log');
186 |
187 | // 'done' callback is not optional.
188 |
189 | return opt;
190 | };
191 |
--------------------------------------------------------------------------------
/lib/reporters/junit.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Nodeunit
3 | * Copyright (c) 2010 Caolan McMahon
4 | * MIT Licensed
5 | */
6 |
7 | /**
8 | * Module dependencies
9 | */
10 |
11 | var nodeunit = require('../nodeunit'),
12 | utils = require('../utils'),
13 | fs = require('fs'),
14 | path = require('path'),
15 | async = require('../../deps/async'),
16 | AssertionError = require('../assert').AssertionError,
17 | child_process = require('child_process'),
18 | ejs = require('ejs');
19 |
20 |
21 | /**
22 | * Reporter info string
23 | */
24 |
25 | exports.info = "jUnit XML test reports";
26 |
27 |
28 | /**
29 | * Ensures a directory exists using mkdir -p.
30 | *
31 | * @param {String} path
32 | * @param {Function} callback
33 | * @api private
34 | */
35 |
36 | var ensureDir = function (path, callback) {
37 | var mkdir = child_process.spawn('mkdir', ['-p', path]);
38 | mkdir.on('error', function (err) {
39 | callback(err);
40 | callback = function(){};
41 | });
42 | mkdir.on('exit', function (code) {
43 | if (code === 0) callback();
44 | else callback(new Error('mkdir exited with code: ' + code));
45 | });
46 | };
47 |
48 |
49 | /**
50 | * Returns absolute version of a path. Relative paths are interpreted
51 | * relative to process.cwd() or the cwd parameter. Paths that are already
52 | * absolute are returned unaltered.
53 | *
54 | * @param {String} p
55 | * @param {String} cwd
56 | * @return {String}
57 | * @api public
58 | */
59 |
60 | var abspath = function (p, /*optional*/cwd) {
61 | if (p[0] === '/') return p;
62 | cwd = cwd || process.cwd();
63 | return path.normalize(path.resolve(p));
64 | };
65 |
66 |
67 | /**
68 | * Run all tests within each module, reporting the results to the command-line,
69 | * then writes out junit-compatible xml documents.
70 | *
71 | * @param {Array} files
72 | * @api public
73 | */
74 |
75 | exports.run = function (files, opts, callback) {
76 | if (!opts.output) {
77 | console.error(
78 | 'Error: No output directory defined.\n' +
79 | '\tEither add an "output" property to your nodeunit.json config ' +
80 | 'file, or\n\tuse the --output command line option.'
81 | );
82 | return;
83 | }
84 | opts.output = abspath(opts.output);
85 | var error = function (str) {
86 | return opts.error_prefix + str + opts.error_suffix;
87 | };
88 | var ok = function (str) {
89 | return opts.ok_prefix + str + opts.ok_suffix;
90 | };
91 | var bold = function (str) {
92 | return opts.bold_prefix + str + opts.bold_suffix;
93 | };
94 |
95 | var start = new Date().getTime();
96 | var paths = files.map(function (p) {
97 | return path.resolve(p);
98 | });
99 |
100 | var modules = {};
101 | var curModule;
102 |
103 | nodeunit.runFiles(paths, {
104 | testspec: opts.testspec,
105 | testFullSpec: opts.testFullSpec,
106 | moduleStart: function (name) {
107 | curModule = {
108 | errorCount: 0,
109 | failureCount: 0,
110 | tests: 0,
111 | testcases: {},
112 | name: name,
113 | start: new Date().getTime()
114 | };
115 | modules[name] = curModule;
116 | },
117 | testStart: function(name) {
118 | curModule.testcases[name] = {name: name, start : new Date().getTime()};
119 | },
120 | moduleDone: function(name) {
121 | curModule.end = new Date().getTime();
122 | },
123 | testDone: function (name, assertions) {
124 | var testcase = curModule.testcases[name];
125 | testcase.end = new Date().getTime();
126 | for (var i=0; i name_slice(['TC1', 'TC1.1', 'mytest'], 1);
105 | * "TC1,TC1.1"
106 | */
107 | var name_slice = function (name_arr, end_index) {
108 | return name_arr.slice(0, end_index + 1).join(",");
109 | };
110 |
111 | var indent = (function () {
112 | var txt = '';
113 | var i;
114 | for (i = 0; i < spaces_per_indent; i++) {
115 | txt += ' ';
116 | }
117 | return txt;
118 | }());
119 |
120 | // Indent once for each indent_level
121 | var add_indent = function (txt, indent_level) {
122 | var k;
123 | for (k = 0; k < indent_level; k++) {
124 | txt += indent;
125 | }
126 | return txt;
127 | };
128 |
129 | // If it's not the last element of the name_arr, it's a testCase.
130 | var is_testCase = function (name_arr, index) {
131 | return index === name_arr.length - 1 ? false : true;
132 | };
133 |
134 | var testCase_line = function (txt) {
135 | return txt + "\n";
136 | };
137 |
138 | /**
139 | * Prints (console.log) the nested test status line(s).
140 | *
141 | * @param {Array} name_arr - Array of name elements.
142 | * @param {String} status - either 'pass' or 'fail'.
143 | * @example
144 | * > print_status(['TC1', 'TC1.1', 'mytest'], 'pass');
145 | * TC1
146 | * TC1.1
147 | * mytest (pass)
148 | */
149 | var print_status = function (name_arr, status) {
150 | var txt = '';
151 | var _name_slice, part, i;
152 | for (i = 0; i < name_arr.length; i++) {
153 | _name_slice = name_slice(name_arr, i);
154 | part = name_arr[i];
155 | if (!tracker.already_printed[_name_slice]) {
156 | txt = add_indent(txt, i);
157 | if (is_testCase(name_arr, i)) {
158 | txt += testCase_line(part);
159 | } else {
160 | txt += status_text(part, status);
161 | }
162 | tracker.already_printed[_name_slice] = true;
163 | }
164 | }
165 | console.log(txt);
166 | };
167 |
168 | nodeunit.runFiles(paths, {
169 | testspec: options.testspec,
170 | testFullSpec: options.testFullSpec,
171 | moduleStart: function (name) {
172 | console.log('\n' + bold(name));
173 | },
174 | testDone: function (name, assertions) {
175 | tracker.remove(name);
176 |
177 | if (!assertions.failures()) {
178 | print_status(name, 'pass');
179 | } else {
180 | print_status(name, 'fail');
181 | assertions.forEach(function (a) {
182 | if (a.failed()) {
183 | a = utils.betterErrors(a);
184 | if (a.error instanceof AssertionError && a.message) {
185 | console.log(
186 | 'Assertion Message: ' +
187 | assertion_message(a.message)
188 | );
189 | }
190 | console.log(a.error.stack + '\n');
191 | }
192 | });
193 | }
194 | },
195 | done: function (assertions, end) {
196 | end = end || new Date().getTime();
197 | var duration = end - start;
198 | if (assertions.failures()) {
199 | console.log(
200 | '\n' + bold(error('FAILURES: ')) + assertions.failures() +
201 | '/' + assertions.length + ' assertions failed (' +
202 | assertions.duration + 'ms)'
203 | );
204 | } else {
205 | console.log(
206 | '\n' + bold(ok('OK: ')) + assertions.length +
207 | ' assertions (' + assertions.duration + 'ms)'
208 | );
209 | }
210 |
211 | if (callback) callback(assertions.failures() ? new Error('We have got test failures.') : undefined);
212 | },
213 | testStart: function (name) {
214 | tracker.put(name);
215 | }
216 | });
217 | };
218 |
--------------------------------------------------------------------------------
/test/test-base.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This module is not a plain nodeunit test suite, but instead uses the
3 | * assert module to ensure a basic level of functionality is present,
4 | * allowing the rest of the tests to be written using nodeunit itself.
5 | *
6 | * THIS FILE SHOULD BE BROWSER-COMPATIBLE JS!
7 | * You can use @REMOVE_LINE_FOR_BROWSER to remove code from the browser build.
8 | * Only code on that line will be removed, its mostly to avoid requiring code
9 | * that is node specific
10 | */
11 |
12 | var assert = require('assert'), // @REMOVE_LINE_FOR_BROWSER
13 | async = require('../deps/async'), // @REMOVE_LINE_FOR_BROWSER
14 | nodeunit = require('../lib/nodeunit'); // @REMOVE_LINE_FOR_BROWSER
15 |
16 |
17 | // NOT A TEST - util function to make testing faster.
18 | // retries the assertion until it passes or the timeout is reached,
19 | // at which point it throws the assertion error
20 | var waitFor = function (fn, timeout, callback, start) {
21 | start = start || new Date().getTime();
22 | callback = callback || function () {};
23 | try {
24 | fn();
25 | callback();
26 | }
27 | catch (e) {
28 | if (e instanceof assert.AssertionError) {
29 | var now = new Date().getTime();
30 | if (now - start >= timeout) {
31 | throw e;
32 | }
33 | else {
34 | async.nextTick(function () {
35 | waitFor(fn, timeout, callback, start);
36 | });
37 | }
38 | }
39 | else {
40 | throw e;
41 | }
42 | }
43 | };
44 |
45 |
46 | // TESTS:
47 |
48 | // Are exported tests actually run? - store completed tests in this variable
49 | // for checking later
50 | var tests_called = {};
51 |
52 | // most basic test that should run, the tests_called object is tested
53 | // at the end of this module to ensure the tests were actually run by nodeunit
54 | exports.testCalled = function (test) {
55 | tests_called.testCalled = true;
56 | test.done();
57 | };
58 |
59 | // generates test functions for nodeunit assertions
60 | var makeTest = function (method, args_pass, args_fail) {
61 | return function (test) {
62 | var test1_called = false;
63 | var test2_called = false;
64 |
65 | // test pass
66 | nodeunit.runTest(
67 | 'testname',
68 | function (test) {
69 | test[method].apply(test, args_pass);
70 | test.done();
71 | },
72 | {testDone: function (name, assertions) {
73 | assert.equal(assertions.length, 1);
74 | assert.equal(assertions.failures(), 0);
75 | }},
76 | function () {
77 | test1_called = true;
78 | }
79 | );
80 |
81 | // test failure
82 | nodeunit.runTest(
83 | 'testname',
84 | function (test) {
85 | test[method].apply(test, args_fail);
86 | test.done();
87 | },
88 | {testDone: function (name, assertions) {
89 | assert.equal(assertions.length, 1);
90 | assert.equal(assertions.failures(), 1);
91 | }},
92 | function () {
93 | test2_called = true;
94 | }
95 | );
96 |
97 | // ensure tests were run
98 | waitFor(function () {
99 | assert.ok(test1_called);
100 | assert.ok(test2_called);
101 | tests_called[method] = true;
102 | }, 500, test.done);
103 | };
104 | };
105 |
106 | // ensure basic assertions are working:
107 | exports.testOk = makeTest('ok', [true], [false]);
108 | exports.testEquals = makeTest('equals', [1, 1], [1, 2]);
109 | exports.testSame = makeTest('same',
110 | [{test: 'test'}, {test: 'test'}],
111 | [{test: 'test'}, {monkey: 'penguin'}]
112 | );
113 |
114 | // from the assert module:
115 | exports.testEqual = makeTest('equal', [1, 1], [1, 2]);
116 | exports.testNotEqual = makeTest('notEqual', [1, 2], [1, 1]);
117 | exports.testDeepEqual = makeTest('deepEqual',
118 | [{one: 1, two: 2}, {one: 1, two: {valueOf:function() {return 2;}}}],
119 | [{one: 1, two: 2}, {two: 2}]
120 | );
121 | exports.testNotDeepEqual = makeTest('notDeepEqual',
122 | [{one: 1}, {two: 2}], [{one: 1}, {one: 1}]
123 | );
124 | exports.testStrictEqual = makeTest('strictEqual', [1, 1], [1, true]);
125 | exports.testNotStrictEqual = makeTest('notStrictEqual', [true, 1], [1, 1]);
126 | exports.testThrows = makeTest('throws',
127 | [function () {
128 | throw new Error('test');
129 | }],
130 | [function () {
131 | return;
132 | }]
133 | );
134 | exports.testThrowsWithReGex = makeTest('throws',
135 | [function () {
136 | throw new Error('test');
137 | }, /test/],
138 | [function () {
139 | throw new Error('test');
140 | }, /fail/]
141 | );
142 | exports.testThrowsWithErrorValidation = makeTest('throws',
143 | [function () {
144 | throw new Error('test');
145 | }, function(err) {
146 | return true;
147 | }],
148 | [function () {
149 | throw new Error('test');
150 | }, function(err) {
151 | return false;
152 | }]
153 | );
154 | exports.testDoesNotThrows = makeTest('doesNotThrow',
155 | [function () {
156 | return;
157 | }],
158 | [function () {
159 | throw new Error('test');
160 | }]
161 | );
162 | exports.testIfError = makeTest('ifError', [false], [new Error('test')]);
163 |
164 |
165 | exports.testExpect = function (test) {
166 | var test1_called = false,
167 | test2_called = false,
168 | test3_called = false;
169 |
170 | // correct number of tests run
171 | nodeunit.runTest(
172 | 'testname',
173 | function (test) {
174 | test.expect(2);
175 | test.ok(true);
176 | test.ok(true);
177 | test.done();
178 | },
179 | {testDone: function (name, assertions) {
180 | test.equals(assertions.length, 2);
181 | test.equals(assertions.failures(), 0);
182 | }},
183 | function () {
184 | test1_called = true;
185 | }
186 | );
187 |
188 | // no tests run
189 | nodeunit.runTest(
190 | 'testname',
191 | function (test) {
192 | test.expect(2);
193 | test.done();
194 | },
195 | {testDone: function (name, assertions) {
196 | test.equals(assertions.length, 1);
197 | test.equals(assertions.failures(), 1);
198 | }},
199 | function () {
200 | test2_called = true;
201 | }
202 | );
203 |
204 | // incorrect number of tests run
205 | nodeunit.runTest(
206 | 'testname',
207 | function (test) {
208 | test.expect(2);
209 | test.ok(true);
210 | test.ok(true);
211 | test.ok(true);
212 | test.done();
213 | },
214 | {testDone: function (name, assertions) {
215 | test.equals(assertions.length, 4);
216 | test.equals(assertions.failures(), 1);
217 | }},
218 | function () {
219 | test3_called = true;
220 | }
221 | );
222 |
223 | // ensure callbacks fired
224 | waitFor(function () {
225 | assert.ok(test1_called);
226 | assert.ok(test2_called);
227 | assert.ok(test3_called);
228 | tests_called.expect = true;
229 | }, 1000, test.done);
230 | };
231 |
232 |
233 | // tests are async, so wait for them to be called
234 | waitFor(function () {
235 | assert.ok(tests_called.testCalled);
236 | assert.ok(tests_called.ok);
237 | assert.ok(tests_called.equals);
238 | assert.ok(tests_called.same);
239 | assert.ok(tests_called.expect);
240 | }, 10000);
241 |
--------------------------------------------------------------------------------
/test/test-testcase.js:
--------------------------------------------------------------------------------
1 | /* THIS FILE SHOULD BE BROWSER-COMPATIBLE JS!
2 | * You can use @REMOVE_LINE_FOR_BROWSER to remove code from the browser build.
3 | * Only code on that line will be removed, its mostly to avoid requiring code
4 | * that is node specific
5 | */
6 |
7 | var nodeunit = require('../lib/nodeunit'); // @REMOVE_LINE_FOR_BROWSER
8 |
9 | exports.testTestCase = function (test) {
10 | test.expect(7);
11 | var call_order = [];
12 | var s = {
13 | setUp: function (callback) {
14 | call_order.push('setUp');
15 | test.equals(this.one, undefined, 'in setUp, this.one not set');
16 | this.one = 1;
17 | callback();
18 | },
19 | tearDown: function (callback) {
20 | call_order.push('tearDown');
21 | test.ok(true, 'tearDown called');
22 | callback();
23 | },
24 | test1: function (t) {
25 | call_order.push('test1');
26 | test.equals(this.one, 1, 'in test1, this.one is 1');
27 | this.one = 2;
28 | t.done();
29 | },
30 | test2: function (t) {
31 | call_order.push('test2');
32 | test.equals(this.one, 1, 'in test2, this.one is still 1');
33 | t.done();
34 | }
35 | };
36 | nodeunit.runSuite(null, s, {}, function () {
37 | test.same(call_order, [
38 | 'setUp', 'test1', 'tearDown',
39 | 'setUp', 'test2', 'tearDown'
40 | ]);
41 | test.done();
42 | });
43 | };
44 |
45 | exports.tearDownAfterError = function (test) {
46 | test.expect(1);
47 | var s = {
48 | tearDown: function (callback) {
49 | test.ok(true, 'tearDown called');
50 | callback();
51 | },
52 | test: function (t) {
53 | throw new Error('some error');
54 | }
55 | };
56 | nodeunit.runSuite(null, s, {}, function () {
57 | test.done();
58 | });
59 | };
60 |
61 | exports.catchSetUpError = function (test) {
62 | test.expect(2);
63 | var test_error = new Error('test error');
64 | var s = {
65 | setUp: function (callback) {
66 | throw test_error;
67 | },
68 | test: function (t) {
69 | test.ok(false, 'test function should not be called');
70 | t.done();
71 | }
72 | };
73 | nodeunit.runSuite(null, s, {}, function (err, assertions) {
74 | test.equal(assertions.length, 1);
75 | test.equal(assertions[0].error, test_error);
76 | test.done();
77 | });
78 | };
79 |
80 | exports.setUpErrorCallback = function (test) {
81 | test.expect(2);
82 | var test_error = new Error('test error');
83 | var s = {
84 | setUp: function (callback) {
85 | callback(test_error);
86 | },
87 | test: function (t) {
88 | test.ok(false, 'test function should not be called');
89 | t.done();
90 | }
91 | };
92 | nodeunit.runSuite(null, s, {}, function (err, assertions) {
93 | test.equal(assertions.length, 1);
94 | test.equal(assertions[0].error, test_error);
95 | test.done();
96 | });
97 | };
98 |
99 | exports.catchTearDownError = function (test) {
100 | test.expect(2);
101 | var test_error = new Error('test error');
102 | var s = {
103 | tearDown: function (callback) {
104 | throw test_error;
105 | },
106 | test: function (t) {
107 | t.done();
108 | }
109 | };
110 | nodeunit.runSuite(null, s, {}, function (err, assertions) {
111 | test.equal(assertions.length, 1);
112 | test.equal(assertions[0].error, test_error);
113 | test.done();
114 | });
115 | };
116 |
117 | exports.tearDownErrorCallback = function (test) {
118 | test.expect(2);
119 | var test_error = new Error('test error');
120 | var s = {
121 | tearDown: function (callback) {
122 | callback(test_error);
123 | },
124 | test: function (t) {
125 | t.done();
126 | }
127 | };
128 | nodeunit.runSuite(null, s, {}, function (err, assertions) {
129 | test.equal(assertions.length, 1);
130 | test.equal(assertions[0].error, test_error);
131 | test.done();
132 | });
133 | };
134 |
135 | exports.testErrorAndtearDownError = function (test) {
136 | test.expect(3);
137 | var error1 = new Error('test error one');
138 | var error2 = new Error('test error two');
139 | var s = {
140 | tearDown: function (callback) {
141 | callback(error2);
142 | },
143 | test: function (t) {
144 | t.done(error1);
145 | }
146 | };
147 | nodeunit.runSuite(null, s, {}, function (err, assertions) {
148 | test.equal(assertions.length, 2);
149 | test.equal(assertions[0].error, error1);
150 | test.equal(assertions[1].error, error2);
151 | test.done();
152 | });
153 | };
154 |
155 | exports.testCaseGroups = function (test) {
156 | var call_order = [];
157 | var s = {
158 | setUp: function (callback) {
159 | call_order.push('setUp');
160 | callback();
161 | },
162 | tearDown: function (callback) {
163 | call_order.push('tearDown');
164 | callback();
165 | },
166 | test1: function (test) {
167 | call_order.push('test1');
168 | test.done();
169 | },
170 | group1: {
171 | test2: function (test) {
172 | call_order.push('group1.test2');
173 | test.done();
174 | }
175 | }
176 | };
177 | nodeunit.runSuite(null, s, {}, function (err, assertions) {
178 | test.same(call_order, [
179 | 'setUp',
180 | 'test1',
181 | 'tearDown',
182 | 'setUp',
183 | 'group1.test2',
184 | 'tearDown'
185 | ]);
186 | test.done();
187 | });
188 | };
189 |
190 | exports.nestedTestCases = function (test) {
191 | var call_order = [];
192 | var s = {
193 | setUp: function (callback) {
194 | call_order.push('setUp');
195 | callback();
196 | },
197 | tearDown: function (callback) {
198 | call_order.push('tearDown');
199 | callback();
200 | },
201 | test1: function (test) {
202 | call_order.push('test1');
203 | test.done();
204 | },
205 | group1: {
206 | setUp: function (callback) {
207 | call_order.push('group1.setUp');
208 | callback();
209 | },
210 | tearDown: function (callback) {
211 | call_order.push('group1.tearDown');
212 | callback();
213 | },
214 | test2: function (test) {
215 | call_order.push('group1.test2');
216 | test.done();
217 | }
218 | }
219 | };
220 | nodeunit.runSuite(null, s, {}, function (err, assertions) {
221 | test.same(call_order, [
222 | 'setUp',
223 | 'test1',
224 | 'tearDown',
225 | 'setUp',
226 | 'group1.setUp',
227 | 'group1.test2',
228 | 'group1.tearDown',
229 | 'tearDown'
230 | ]);
231 | test.done();
232 | });
233 | };
234 |
235 | exports.deepNestedTestCases = function (test) {
236 | var val = 'foo';
237 | var s = {
238 | setUp: function (callback) {
239 | val = 'bar';
240 | callback();
241 | },
242 | group1: {
243 | test: {
244 | test2: function (test) {
245 | test.equal(val, 'bar');
246 | test.done();
247 | }
248 | }
249 | }
250 | };
251 | nodeunit.runSuite(null, s, {}, function (err, assertions) {
252 | test.ok(!assertions[0].failed());
253 | test.equal(assertions.length, 1);
254 | test.done();
255 | });
256 | };
257 |
--------------------------------------------------------------------------------
/test/test-runfiles.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert'),
2 | fs = require('fs'),
3 | path = require('path'),
4 | nodeunit = require('../lib/nodeunit');
5 |
6 |
7 | var setup = function (fn) {
8 | return function (test) {
9 | process.chdir(__dirname);
10 | var env = {
11 | mock_module1: require(__dirname + '/fixtures/mock_module1'),
12 | mock_module2: require(__dirname + '/fixtures/mock_module2'),
13 | mock_module3: require(__dirname + '/fixtures/dir/mock_module3'),
14 | mock_module4: require(__dirname + '/fixtures/dir/mock_module4')
15 | };
16 | fn.call(env, test);
17 | };
18 | };
19 |
20 |
21 | exports.testRunFiles = setup(function (test) {
22 | test.expect(33);
23 | var runModule_copy = nodeunit.runModule;
24 |
25 | var runModule_calls = [];
26 | var modules = [];
27 |
28 | var opts = {
29 | moduleStart: function () {
30 | return 'moduleStart';
31 | },
32 | testDone: function () {
33 | return 'testDone';
34 | },
35 | testReady: function () {
36 | return 'testReady';
37 | },
38 | testStart: function () {
39 | return 'testStart';
40 | },
41 | log: function () {
42 | return 'log';
43 | },
44 | done: function (assertions) {
45 | test.equals(assertions.failures(), 0, 'failures');
46 | test.equals(assertions.length, 5, 'length');
47 | test.ok(typeof assertions.duration === "number");
48 |
49 | var called_with = function (name) {
50 | return runModule_calls.some(function (m) {
51 | return m.name === name;
52 | });
53 | };
54 | test.ok(called_with('mock_module1'), 'mock_module1 ran');
55 | test.ok(called_with('mock_module2'), 'mock_module2 ran');
56 | test.ok(called_with('mock_module3'), 'mock_module3 ran');
57 | test.ok(called_with('mock_module4'), 'mock_module4 ran');
58 | test.equals(runModule_calls.length, 5);
59 |
60 | nodeunit.runModule = runModule_copy;
61 | test.done();
62 | }
63 | };
64 |
65 | nodeunit.runModule = function (name, mod, options, callback) {
66 | test.equals(options.testDone, opts.testDone);
67 | test.equals(options.testReady, opts.testReady);
68 | test.equals(options.testStart, opts.testStart);
69 | test.equals(options.log, opts.log);
70 | test.ok(typeof name === "string");
71 | runModule_calls.push(mod);
72 | var m = [{failed: function () {
73 | return false;
74 | }}];
75 | modules.push(m);
76 | callback(null, m);
77 | };
78 |
79 | nodeunit.runFiles(
80 | [__dirname + '/fixtures/mock_module1.js',
81 | __dirname + '/fixtures/mock_module2.js',
82 | __dirname + '/fixtures/dir'],
83 | opts
84 | );
85 | });
86 |
87 | exports.testRunFilesEmpty = function (test) {
88 | test.expect(3);
89 | nodeunit.runFiles([], {
90 | moduleStart: function () {
91 | test.ok(false, 'should not be called');
92 | },
93 | testDone: function () {
94 | test.ok(false, 'should not be called');
95 | },
96 | testReady: function () {
97 | test.ok(false, 'should not be called');
98 | },
99 | testStart: function () {
100 | test.ok(false, 'should not be called');
101 | },
102 | log: function () {
103 | test.ok(false, 'should not be called');
104 | },
105 | done: function (assertions) {
106 | test.equals(assertions.failures(), 0, 'failures');
107 | test.equals(assertions.length, 0, 'length');
108 | test.ok(typeof assertions.duration === "number");
109 | test.done();
110 | }
111 | });
112 | };
113 |
114 |
115 | exports.testEmptyDir = function (test) {
116 | var dir2 = __dirname + '/fixtures/dir2';
117 |
118 | // git doesn't like empty directories, so we have to create one
119 | fs.access(dir2, function (err) {
120 | if (err) {
121 | fs.mkdirSync(dir2, 0777);
122 | }
123 |
124 | // runFiles on empty directory:
125 | nodeunit.runFiles([dir2], {
126 | moduleStart: function () {
127 | test.ok(false, 'should not be called');
128 | },
129 | testDone: function () {
130 | test.ok(false, 'should not be called');
131 | },
132 | testReady: function () {
133 | test.ok(false, 'should not be called');
134 | },
135 | testStart: function () {
136 | test.ok(false, 'should not be called');
137 | },
138 | log: function () {
139 | test.ok(false, 'should not be called');
140 | },
141 | done: function (assertions) {
142 | test.equals(assertions.failures(), 0, 'failures');
143 | test.equals(assertions.length, 0, 'length');
144 | test.ok(typeof assertions.duration === "number");
145 | test.done();
146 | }
147 | });
148 | });
149 | };
150 |
151 |
152 | var CoffeeScript;
153 | try {
154 | CoffeeScript = require('coffee-script');
155 | if (CoffeeScript.register != null) {
156 | CoffeeScript.register();
157 | }
158 | } catch (e) {
159 | }
160 |
161 | if (CoffeeScript) {
162 | exports.testCoffeeScript = function (test) {
163 | process.chdir(__dirname);
164 | var env = {
165 | mock_coffee_module: require(__dirname +
166 | '/fixtures/coffee/mock_coffee_module')
167 | };
168 |
169 | test.expect(10);
170 | var runModule_copy = nodeunit.runModule;
171 |
172 | var runModule_calls = [];
173 | var modules = [];
174 |
175 | var opts = {
176 | moduleStart: function () {
177 | return 'moduleStart';
178 | },
179 | testDone: function () {
180 | return 'testDone';
181 | },
182 | testReady: function () {
183 | return 'testReady';
184 | },
185 | testStart: function () {
186 | return 'testStart';
187 | },
188 | log: function () {
189 | return 'log';
190 | },
191 | done: function (assertions) {
192 | test.equals(assertions.failures(), 0, 'failures');
193 | test.equals(assertions.length, 1, 'length');
194 | test.ok(typeof assertions.duration === "number");
195 |
196 | var called_with = function (name) {
197 | return runModule_calls.some(function (m) {
198 | return m.name === name;
199 | });
200 | };
201 | test.ok(
202 | called_with('mock_coffee_15'),
203 | 'mock_coffee_module ran'
204 | );
205 | test.equals(runModule_calls.length, 1);
206 |
207 | nodeunit.runModule = runModule_copy;
208 | test.done();
209 | }
210 | };
211 |
212 | nodeunit.runModule = function (name, mod, options, callback) {
213 | test.equals(options.testDone, opts.testDone);
214 | test.equals(options.testReady, opts.testReady);
215 | test.equals(options.testStart, opts.testStart);
216 | test.equals(options.log, opts.log);
217 | test.ok(typeof name === "string");
218 | runModule_calls.push(mod);
219 | var m = [{failed: function () {
220 | return false;
221 | }}];
222 | modules.push(m);
223 | callback(null, m);
224 | };
225 |
226 | nodeunit.runFiles(
227 | [__dirname + '/fixtures/coffee/mock_coffee_module.coffee'],
228 | opts
229 | );
230 | };
231 | }
232 |
--------------------------------------------------------------------------------
/test/test-testcase-legacy.js:
--------------------------------------------------------------------------------
1 | /* THIS FILE SHOULD BE BROWSER-COMPATIBLE JS!
2 | * You can use @REMOVE_LINE_FOR_BROWSER to remove code from the browser build.
3 | * Only code on that line will be removed, its mostly to avoid requiring code
4 | * that is node specific
5 | */
6 |
7 | var nodeunit = require('../lib/nodeunit'); // @REMOVE_LINE_FOR_BROWSER
8 | var testCase = nodeunit.testCase;
9 |
10 | exports.testTestCase = function (test) {
11 | test.expect(7);
12 | var call_order = [];
13 | var s = testCase({
14 | setUp: function (callback) {
15 | call_order.push('setUp');
16 | test.equals(this.one, undefined);
17 | this.one = 1;
18 | callback();
19 | },
20 | tearDown: function (callback) {
21 | call_order.push('tearDown');
22 | test.ok(true, 'tearDown called');
23 | callback();
24 | },
25 | test1: function (t) {
26 | call_order.push('test1');
27 | test.equals(this.one, 1);
28 | this.one = 2;
29 | t.done();
30 | },
31 | test2: function (t) {
32 | call_order.push('test2');
33 | test.equals(this.one, 1);
34 | t.done();
35 | }
36 | });
37 | nodeunit.runSuite(null, s, {}, function () {
38 | test.same(call_order, [
39 | 'setUp', 'test1', 'tearDown',
40 | 'setUp', 'test2', 'tearDown'
41 | ]);
42 | test.done();
43 | });
44 | };
45 |
46 | exports.tearDownAfterError = function (test) {
47 | test.expect(1);
48 | var s = testCase({
49 | tearDown: function (callback) {
50 | test.ok(true, 'tearDown called');
51 | callback();
52 | },
53 | test: function (t) {
54 | throw new Error('some error');
55 | }
56 | });
57 | nodeunit.runSuite(null, s, {}, function () {
58 | test.done();
59 | });
60 | };
61 |
62 | exports.catchSetUpError = function (test) {
63 | test.expect(2);
64 | var test_error = new Error('test error');
65 | var s = testCase({
66 | setUp: function (callback) {
67 | throw test_error;
68 | },
69 | test: function (t) {
70 | test.ok(false, 'test function should not be called');
71 | t.done();
72 | }
73 | });
74 | nodeunit.runSuite(null, s, {}, function (err, assertions) {
75 | test.equal(assertions.length, 1);
76 | test.equal(assertions[0].error, test_error);
77 | test.done();
78 | });
79 | };
80 |
81 | exports.setUpErrorCallback = function (test) {
82 | test.expect(2);
83 | var test_error = new Error('test error');
84 | var s = testCase({
85 | setUp: function (callback) {
86 | callback(test_error);
87 | },
88 | test: function (t) {
89 | test.ok(false, 'test function should not be called');
90 | t.done();
91 | }
92 | });
93 | nodeunit.runSuite(null, s, {}, function (err, assertions) {
94 | test.equal(assertions.length, 1);
95 | test.equal(assertions[0].error, test_error);
96 | test.done();
97 | });
98 | };
99 |
100 | exports.catchTearDownError = function (test) {
101 | test.expect(2);
102 | var test_error = new Error('test error');
103 | var s = testCase({
104 | tearDown: function (callback) {
105 | throw test_error;
106 | },
107 | test: function (t) {
108 | t.done();
109 | }
110 | });
111 | nodeunit.runSuite(null, s, {}, function (err, assertions) {
112 | test.equal(assertions.length, 1);
113 | test.equal(assertions[0].error, test_error);
114 | test.done();
115 | });
116 | };
117 |
118 | exports.tearDownErrorCallback = function (test) {
119 | test.expect(2);
120 | var test_error = new Error('test error');
121 | var s = testCase({
122 | tearDown: function (callback) {
123 | callback(test_error);
124 | },
125 | test: function (t) {
126 | t.done();
127 | }
128 | });
129 | nodeunit.runSuite(null, s, {}, function (err, assertions) {
130 | test.equal(assertions.length, 1);
131 | test.equal(assertions[0].error, test_error);
132 | test.done();
133 | });
134 | };
135 |
136 | exports.testErrorAndtearDownError = function (test) {
137 | test.expect(3);
138 | var error1 = new Error('test error one');
139 | var error2 = new Error('test error two');
140 | var s = testCase({
141 | tearDown: function (callback) {
142 | callback(error2);
143 | },
144 | test: function (t) {
145 | t.done(error1);
146 | }
147 | });
148 | nodeunit.runSuite(null, s, {}, function (err, assertions) {
149 | test.equal(assertions.length, 2);
150 | test.equal(assertions[0].error, error1);
151 | test.equal(assertions[1].error, error2);
152 | test.done();
153 | });
154 | };
155 |
156 | exports.testCaseGroups = function (test) {
157 | var call_order = [];
158 | var s = testCase({
159 | setUp: function (callback) {
160 | call_order.push('setUp');
161 | callback();
162 | },
163 | tearDown: function (callback) {
164 | call_order.push('tearDown');
165 | callback();
166 | },
167 | test1: function (test) {
168 | call_order.push('test1');
169 | test.done();
170 | },
171 | group1: {
172 | test2: function (test) {
173 | call_order.push('group1.test2');
174 | test.done();
175 | }
176 | }
177 | });
178 | nodeunit.runSuite(null, s, {}, function (err, assertions) {
179 | test.same(call_order, [
180 | 'setUp',
181 | 'test1',
182 | 'tearDown',
183 | 'setUp',
184 | 'group1.test2',
185 | 'tearDown'
186 | ]);
187 | test.done();
188 | });
189 | };
190 |
191 | exports.nestedTestCases = function (test) {
192 | var call_order = [];
193 | var s = testCase({
194 | setUp: function (callback) {
195 | call_order.push('setUp');
196 | callback();
197 | },
198 | tearDown: function (callback) {
199 | call_order.push('tearDown');
200 | callback();
201 | },
202 | test1: function (test) {
203 | call_order.push('test1');
204 | test.done();
205 | },
206 | group1: testCase({
207 | setUp: function (callback) {
208 | call_order.push('group1.setUp');
209 | callback();
210 | },
211 | tearDown: function (callback) {
212 | call_order.push('group1.tearDown');
213 | callback();
214 | },
215 | test2: function (test) {
216 | call_order.push('group1.test2');
217 | test.done();
218 | }
219 | })
220 | });
221 | nodeunit.runSuite(null, s, {}, function (err, assertions) {
222 | test.same(call_order, [
223 | 'setUp',
224 | 'test1',
225 | 'tearDown',
226 | 'setUp',
227 | 'group1.setUp',
228 | 'group1.test2',
229 | 'group1.tearDown',
230 | 'tearDown'
231 | ]);
232 | test.done();
233 | });
234 | };
235 |
236 | exports.deepNestedTestCases = function (test) {
237 | var val = 'foo';
238 | var s = testCase({
239 | setUp: function (callback) {
240 | val = 'bar';
241 | callback();
242 | },
243 | group1: testCase({
244 | test: testCase({
245 | test2: function (test) {
246 | test.equal(val, 'bar');
247 | test.done();
248 | }
249 | })
250 | })
251 | });
252 | nodeunit.runSuite(null, s, {}, function (err, assertions) {
253 | test.ok(!assertions[0].failed());
254 | test.equal(assertions.length, 1);
255 | test.done();
256 | });
257 | };
258 |
--------------------------------------------------------------------------------
/lib/utils.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Nodeunit
3 | * Copyright (c) 2010 Caolan McMahon
4 | * MIT Licensed
5 | */
6 |
7 | /**
8 | * Module dependencies
9 | */
10 |
11 | var async = require('../deps/async'),
12 | fs = require('fs'),
13 | util = require('util'),
14 | Script = require('vm').Script,
15 | http = require('http'),
16 | path = require('path');
17 |
18 |
19 | /**
20 | * Detect if coffee-script, iced-coffeescript, or streamline are available and
21 | * the respective file extensions to the search filter in modulePaths if it is.
22 | */
23 |
24 | var extensions = [ 'js' ]; // js is always supported: add it unconditionally
25 | var extensionPattern;
26 |
27 | try {
28 | require('coffee' + '-script/register');
29 | extensions.push('coffee');
30 | } catch (e) { }
31 |
32 | try {
33 | require('iced-coffee' + '-script/register');
34 | extensions.push('iced');
35 | } catch (e) { }
36 |
37 | try {
38 | require('stream' + 'line').register();
39 | extensions.push('_coffee');
40 | extensions.push('_js');
41 | } catch (e) { }
42 |
43 | extensionPattern = new RegExp('\\.(?:' + extensions.join('|') + ')$');
44 |
45 |
46 | /**
47 | * Finds all modules at each path in an array, If a path is a directory, it
48 | * returns all supported file types inside it. This only reads 1 level deep in
49 | * the directory and does not recurse through sub-directories.
50 | *
51 | * The extension (.js, .coffee etc) is stripped from the filenames so they can
52 | * simply be require()'ed.
53 | *
54 | * @param {Array} paths
55 | * @param {Function} callback
56 | * @param {Boolean=} recursive
57 | * @api public
58 | */
59 | exports.modulePaths = function modulePaths(paths, callback, recursive) {
60 | recursive = (recursive === true);
61 | async.concatSeries(paths, function (p, cb) {
62 | fs.stat(p, function (err, stats) {
63 | if (err) {
64 | return cb(err);
65 | }
66 | if (stats.isFile()) {
67 | return cb(null, [p]);
68 | }
69 | if (stats.isDirectory()) {
70 | fs.readdir(p, function (err, files) {
71 | if (err) {
72 | return cb(err);
73 | }
74 |
75 | // filter out any filenames with unsupported extensions
76 | var modules = files.filter(function (filename) {
77 | return extensionPattern.exec(filename);
78 | });
79 |
80 | // remove extension from module name and prepend the
81 | // directory path
82 | var fullpaths = modules.map(function (filename) {
83 | var mod_name = filename.replace(extensionPattern, '');
84 | return [p, mod_name].join('/');
85 | });
86 |
87 | if (recursive) {
88 | // get all sub directories
89 | var directories =
90 | files
91 | .map(function(filename) {
92 | // resolve path first
93 | return path.resolve(p, filename);
94 | })
95 | .filter(function(filename) {
96 | // fetch only directories
97 | return (fs.statSync(filename).isDirectory());
98 | });
99 |
100 | // recursively call modulePaths() with sub directories
101 | modulePaths(directories, function(err, files) {
102 | if (!err) {
103 | cb(null, fullpaths.concat(files).sort())
104 | } else {
105 | cb(err);
106 | }
107 | }, recursive);
108 | } else {
109 | // sort filenames here, because Array.map changes order
110 | fullpaths.sort();
111 |
112 | // finish
113 | cb(null, fullpaths);
114 | }
115 |
116 | });
117 | }
118 | });
119 | }, callback);
120 | };
121 |
122 | /**
123 | * Evaluates JavaScript files in a sandbox, returning the context. The first
124 | * argument can either be a single filename or an array of filenames. If
125 | * multiple filenames are given their contents are concatenated before
126 | * evalution. The second argument is an optional context to use for the sandbox.
127 | *
128 | * @param files
129 | * @param {Object} sandbox
130 | * @return {Object}
131 | * @api public
132 | */
133 |
134 | exports.sandbox = function (files, /*optional*/sandbox) {
135 | var source, script, result;
136 | if (!(files instanceof Array)) {
137 | files = [files];
138 | }
139 | source = files.map(function (file) {
140 | return fs.readFileSync(file, 'utf8');
141 | }).join('');
142 |
143 | if (!sandbox) {
144 | sandbox = {};
145 | }
146 | script = new Script(source);
147 | result = script.runInNewContext(sandbox);
148 | return sandbox;
149 | };
150 |
151 | /**
152 | * Provides a http request, response testing environment.
153 | *
154 | * Example:
155 | *
156 | * var httputil = require('nodeunit').utils.httputil
157 | * exports.testSomething = function(test) {
158 | * httputil(function (req, resp) {
159 | * resp.writeHead(200, {});
160 | * resp.end('test data');
161 | * },
162 | * function(server, client) {
163 | * client.fetch('GET', '/', {}, function(resp) {
164 | * test.equal('test data', resp.body);
165 | * server.close();
166 | * test.done();
167 | * })
168 | * });
169 | * };
170 | *
171 | * @param {Function} cgi
172 | * @param {Function} envReady
173 | * @api public
174 | */
175 | exports.httputil = function (cgi, envReady) {
176 | var hostname = process.env.HOSTNAME || 'localhost';
177 | var port = process.env.PORT || 3000;
178 |
179 | var server = http.createServer(cgi);
180 | server.listen(port, hostname);
181 |
182 | var agent = new http.Agent({ host: hostname, port: port, maxSockets: 1 });
183 | var client = {
184 | fetch: function (method, path, headers, respReady) {
185 | var request = http.request({
186 | host: hostname,
187 | port: port,
188 | agent: agent,
189 | method: method,
190 | path: path,
191 | headers: headers
192 | });
193 | request.end();
194 | request.on('response', function (response) {
195 | response.setEncoding('utf8');
196 | response.on('data', function (chunk) {
197 | if (response.body) {
198 | response.body += chunk;
199 | } else {
200 | response.body = chunk;
201 | }
202 | });
203 | response.on('end', function () {
204 | if (response.headers['content-type'] === 'application/json') {
205 | response.bodyAsObject = JSON.parse(response.body);
206 | }
207 | respReady(response);
208 | });
209 | });
210 | }
211 | };
212 |
213 | process.nextTick(function () {
214 | if (envReady && typeof envReady === 'function') {
215 | envReady(server, client);
216 | }
217 | });
218 | };
219 |
220 |
221 | /**
222 | * Improves formatting of AssertionError messages to make deepEqual etc more
223 | * readable.
224 | *
225 | * @param {Object} assertion
226 | * @return {Object}
227 | * @api public
228 | */
229 |
230 | exports.betterErrors = function (assertion) {
231 | if (!assertion.error) {
232 | return assertion;
233 | }
234 | var e = assertion.error;
235 |
236 | if (typeof e.actual !== 'undefined' && typeof e.expected !== 'undefined') {
237 | var actual = util.inspect(e.actual, false, 10).replace(/\n$/, '');
238 | var expected = util.inspect(e.expected, false, 10).replace(/\n$/, '');
239 |
240 | var multiline = (
241 | actual.indexOf('\n') !== -1 ||
242 | expected.indexOf('\n') !== -1
243 | );
244 | var spacing = (multiline ? '\n' : ' ');
245 | e._message = e.message;
246 | e.stack = (
247 | e.name + ':' + spacing +
248 | actual + spacing + e.operator + spacing +
249 | expected + '\n' +
250 | e.stack.split('\n').slice(1).join('\n')
251 | );
252 | }
253 | return assertion;
254 | };
255 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | PACKAGE = nodeunit
2 | NODEJS = $(if $(shell test -f /usr/bin/nodejs && echo "true"),nodejs,node)
3 |
4 | PREFIX ?= /usr/local
5 | BINDIR ?= $(PREFIX)/bin
6 | DATADIR ?= $(PREFIX)/share
7 | MANDIR ?= $(PREFIX)/share/man
8 | LIBDIR ?= $(PREFIX)/lib
9 | NODEJSLIBDIR ?= $(LIBDIR)/$(NODEJS)
10 |
11 | BUILDDIR = dist
12 |
13 | DOCS = $(shell find doc -name '*.md' \
14 | |sed 's|.md|.1|g' \
15 | |sed 's|doc/|man1/|g' \
16 | )
17 |
18 |
19 | $(shell if [ ! -d $(BUILDDIR) ]; then mkdir $(BUILDDIR); fi)
20 |
21 | all: build doc
22 |
23 | browser:
24 | # super hacky build script for browser version!
25 | mkdir -p $(BUILDDIR)/browser
26 | rm -rf $(BUILDDIR)/browser/*
27 | # build browser version of nodeunit.js
28 | cat share/license.js >> $(BUILDDIR)/browser/nodeunit.js
29 | echo "nodeunit = (function(){" >> $(BUILDDIR)/browser/nodeunit.js
30 | cat deps/json2.js >> $(BUILDDIR)/browser/nodeunit.js
31 | # make assert global
32 | echo "var assert = this.assert = {};" >> $(BUILDDIR)/browser/nodeunit.js
33 | echo "var types = {};" >> $(BUILDDIR)/browser/nodeunit.js
34 | echo "var core = {};" >> $(BUILDDIR)/browser/nodeunit.js
35 | echo "var nodeunit = {};" >> $(BUILDDIR)/browser/nodeunit.js
36 | echo "var reporter = {};" >> $(BUILDDIR)/browser/nodeunit.js
37 | cat deps/async.js >> $(BUILDDIR)/browser/nodeunit.js
38 | echo "(function(exports){" >> $(BUILDDIR)/browser/nodeunit.js
39 | cat lib/assert.js >> $(BUILDDIR)/browser/nodeunit.js
40 | echo "})(assert);" >> $(BUILDDIR)/browser/nodeunit.js
41 | echo "(function(exports){" >> $(BUILDDIR)/browser/nodeunit.js
42 | cat lib/types.js >> $(BUILDDIR)/browser/nodeunit.js
43 | echo "})(types);" >> $(BUILDDIR)/browser/nodeunit.js
44 | echo "(function(exports){" >> $(BUILDDIR)/browser/nodeunit.js
45 | cat lib/core.js >> $(BUILDDIR)/browser/nodeunit.js
46 | echo "})(core);" >> $(BUILDDIR)/browser/nodeunit.js
47 | echo "(function(exports){" >> $(BUILDDIR)/browser/nodeunit.js
48 | cat lib/reporters/browser.js >> $(BUILDDIR)/browser/nodeunit.js
49 | echo "})(reporter);" >> $(BUILDDIR)/browser/nodeunit.js
50 | echo "nodeunit = core;" >> $(BUILDDIR)/browser/nodeunit.js
51 | echo "nodeunit.assert = assert;" >> $(BUILDDIR)/browser/nodeunit.js
52 | echo "nodeunit.reporter = reporter;" >> $(BUILDDIR)/browser/nodeunit.js
53 | echo "nodeunit.run = reporter.run;" >> $(BUILDDIR)/browser/nodeunit.js
54 | echo "return nodeunit; })();" >> $(BUILDDIR)/browser/nodeunit.js
55 | cp $(BUILDDIR)/browser/nodeunit.js $(BUILDDIR)/browser/.nodeunit.js
56 | sed "/\@REMOVE_LINE_FOR_BROWSER/d" <$(BUILDDIR)/browser/.nodeunit.js > $(BUILDDIR)/browser/nodeunit.js
57 | rm $(BUILDDIR)/browser/.nodeunit.js
58 | # copy nodeunit.css
59 | cp share/nodeunit.css $(BUILDDIR)/browser/nodeunit.css
60 | # create nodeunit.min.js
61 | node_modules/uglify-js/bin/uglifyjs $(BUILDDIR)/browser/nodeunit.js > $(BUILDDIR)/browser/nodeunit.min.js
62 | # create test scripts
63 | mkdir -p $(BUILDDIR)/browser/test
64 | cp test/test.html $(BUILDDIR)/browser/test/test.html
65 | # test-base.js
66 | echo "(function (exports) {" > $(BUILDDIR)/browser/test/test-base.js
67 | cat test/test-base.js >> $(BUILDDIR)/browser/test/test-base.js
68 | echo "})(this.test_base = {});" >> $(BUILDDIR)/browser/test/test-base.js
69 | cp $(BUILDDIR)/browser/test/test-base.js $(BUILDDIR)/browser/.test-base.js
70 | sed "/\@REMOVE_LINE_FOR_BROWSER/d" <$(BUILDDIR)/browser/.test-base.js > $(BUILDDIR)/browser/test/test-base.js
71 | rm $(BUILDDIR)/browser/.test-base.js
72 | # test-runmodule.js
73 | echo "(function (exports) {" > $(BUILDDIR)/browser/test/test-runmodule.js
74 | cat test/test-runmodule.js >> $(BUILDDIR)/browser/test/test-runmodule.js
75 | echo "})(this.test_runmodule = {});" >> $(BUILDDIR)/browser/test/test-runmodule.js
76 | cp $(BUILDDIR)/browser/test/test-runmodule.js $(BUILDDIR)/browser/.test-runmodule.js
77 | sed "/\@REMOVE_LINE_FOR_BROWSER/d" <$(BUILDDIR)/browser/.test-runmodule.js > $(BUILDDIR)/browser/test/test-runmodule.js
78 | rm $(BUILDDIR)/browser/.test-runmodule.js
79 | # test-runtest.js
80 | echo "(function (exports) {" > $(BUILDDIR)/browser/test/test-runtest.js
81 | cat test/test-runtest.js >> $(BUILDDIR)/browser/test/test-runtest.js
82 | echo "})(this.test_runtest = {});" >> $(BUILDDIR)/browser/test/test-runtest.js
83 | cp $(BUILDDIR)/browser/test/test-runtest.js $(BUILDDIR)/browser/.test-runtest.js
84 | sed "/\@REMOVE_LINE_FOR_BROWSER/d" <$(BUILDDIR)/browser/.test-runtest.js > $(BUILDDIR)/browser/test/test-runtest.js
85 | rm $(BUILDDIR)/browser/.test-runtest.js
86 | # test-testcase.js
87 | echo "(function (exports) {" > $(BUILDDIR)/browser/test/test-testcase.js
88 | cat test/test-testcase.js >> $(BUILDDIR)/browser/test/test-testcase.js
89 | echo "})(this.test_testcase = {});" >> $(BUILDDIR)/browser/test/test-testcase.js
90 | cp $(BUILDDIR)/browser/test/test-testcase.js $(BUILDDIR)/browser/.test-testcase.js
91 | sed "/\@REMOVE_LINE_FOR_BROWSER/d" <$(BUILDDIR)/browser/.test-testcase.js > $(BUILDDIR)/browser/test/test-testcase.js
92 | rm $(BUILDDIR)/browser/.test-testcase.js
93 | # test-testcase-legacy.js
94 | echo "(function (exports) {" > $(BUILDDIR)/browser/test/test-testcase-legacy.js
95 | cat test/test-testcase-legacy.js >> $(BUILDDIR)/browser/test/test-testcase-legacy.js
96 | echo "})(this.test_testcase_legacy = {});" >> $(BUILDDIR)/browser/test/test-testcase-legacy.js
97 | cp $(BUILDDIR)/browser/test/test-testcase-legacy.js $(BUILDDIR)/browser/.test-testcase-legacy.js
98 | sed "/\@REMOVE_LINE_FOR_BROWSER/d" <$(BUILDDIR)/browser/.test-testcase-legacy.js > $(BUILDDIR)/browser/test/test-testcase-legacy.js
99 | rm $(BUILDDIR)/browser/.test-testcase-legacy.js
100 | # copy nodeunit.js to dist/browser/test to make it easier for me to host and
101 | # run on windows VMs with IE
102 | cp $(BUILDDIR)/browser/nodeunit.js $(BUILDDIR)/browser/test/nodeunit.js
103 | cp $(BUILDDIR)/browser/nodeunit.css $(BUILDDIR)/browser/test/nodeunit.css
104 |
105 | commonjs:
106 | # super hacky build script for browser commonjs version!
107 | ##### make commonjs browser module ######
108 | mkdir -p $(BUILDDIR)/commonjs
109 | rm -rf $(BUILDDIR)/commonjs/*
110 | mkdir -p $(BUILDDIR)/commonjs/deps
111 | cp deps/json2.js $(BUILDDIR)/commonjs/deps
112 | cp deps/async.js $(BUILDDIR)/commonjs/deps
113 | echo "var async = require('async');" >> $(BUILDDIR)/commonjs/nodeunit.js
114 | echo "var assert = {};" >> $(BUILDDIR)/commonjs/nodeunit.js
115 | echo "var types = {};" >> $(BUILDDIR)/commonjs/nodeunit.js
116 | echo "var core = {};" >> $(BUILDDIR)/commonjs/nodeunit.js
117 | echo "var nodeunit = {};" >> $(BUILDDIR)/commonjs/nodeunit.js
118 | echo "var reporter = {};" >> $(BUILDDIR)/commonjs/nodeunit.js
119 | echo "(function(exports){" >> $(BUILDDIR)/commonjs/nodeunit.js
120 | cat lib/assert.js >> $(BUILDDIR)/commonjs/nodeunit.js
121 | echo "})(assert);" >> $(BUILDDIR)/commonjs/nodeunit.js
122 | echo "(function(exports){" >> $(BUILDDIR)/commonjs/nodeunit.js
123 | cat lib/types.js >> $(BUILDDIR)/commonjs/nodeunit.js
124 | echo "})(types);" >> $(BUILDDIR)/commonjs/nodeunit.js
125 | echo "(function(exports){" >> $(BUILDDIR)/commonjs/nodeunit.js
126 | cat lib/core.js >> $(BUILDDIR)/commonjs/nodeunit.js
127 | echo "})(core);" >> $(BUILDDIR)/commonjs/nodeunit.js
128 | echo "module.exports = core;" >> $(BUILDDIR)/commonjs/nodeunit.js
129 | echo "(function(exports, nodeunit){" >> $(BUILDDIR)/commonjs/nodeunit.js
130 | cat lib/reporters/browser.js >> $(BUILDDIR)/commonjs/nodeunit.js
131 | echo "})(reporter, module.exports);" >> $(BUILDDIR)/commonjs/nodeunit.js
132 | echo "module.exports.assert = assert;" >> $(BUILDDIR)/commonjs/nodeunit.js
133 | echo "module.exports.reporter = reporter;" >> $(BUILDDIR)/commonjs/nodeunit.js
134 | echo "module.exports.run = reporter.run;" >> $(BUILDDIR)/commonjs/nodeunit.js
135 | sed -i "/\@REMOVE_LINE_FOR_BROWSER/d" $(BUILDDIR)/commonjs/nodeunit.js
136 | sed -i "/\@REMOVE_LINE_FOR_COMMONJS/d" $(BUILDDIR)/commonjs/nodeunit.js
137 | ##### end of commonjs browser module #####
138 |
139 | build: stamp-build
140 |
141 | stamp-build: $(wildcard deps/* lib/*.js)
142 | touch $@;
143 | mkdir -p $(BUILDDIR)/nodeunit
144 | cp -R bin node_modules deps index.js lib package.json share $(BUILDDIR)/nodeunit
145 | printf '#!/bin/sh\n$(NODEJS) $(NODEJSLIBDIR)/$(PACKAGE)/bin/nodeunit $$@' > $(BUILDDIR)/nodeunit.sh
146 |
147 | test:
148 | $(NODEJS) ./bin/nodeunit test
149 |
150 | install: build
151 | install -d $(NODEJSLIBDIR)
152 | cp -a $(BUILDDIR)/nodeunit $(NODEJSLIBDIR)
153 | install -m 0755 $(BUILDDIR)/nodeunit.sh $(BINDIR)/nodeunit
154 | install -d $(MANDIR)/man1/
155 | cp -a man1/nodeunit.1 $(MANDIR)/man1/
156 |
157 | uninstall:
158 | rm -rf $(NODEJSLIBDIR)/nodeunit $(NODEJSLIBDIR)/nodeunit.js $(BINDIR)/nodeunit
159 | rm -rf $(MANDIR)/man1/nodeunit.1
160 |
161 | clean:
162 | rm -rf $(BUILDDIR) stamp-build
163 |
164 | lint:
165 | nodelint --config nodelint.cfg ./index.js ./bin/nodeunit ./bin/nodeunit.json ./lib/*.js ./lib/reporters/*.js ./test/*.js
166 |
167 | doc: man1 $(DOCS)
168 | @true
169 |
170 | man1:
171 | @if ! test -d man1 ; then mkdir -p man1 ; fi
172 |
173 | # use `npm install ronn` for this to work.
174 | man1/%.1: doc/%.md
175 | ronn --roff $< > $@
176 |
177 | .PHONY: browser test install uninstall build all
178 |
--------------------------------------------------------------------------------
/test/test-runmodule.js:
--------------------------------------------------------------------------------
1 | /* THIS FILE SHOULD BE BROWSER-COMPATIBLE JS!
2 | * You can use @REMOVE_LINE_FOR_BROWSER to remove code from the browser build.
3 | * Only code on that line will be removed, its mostly to avoid requiring code
4 | * that is node specific
5 | */
6 |
7 | var nodeunit = require('../lib/nodeunit'); // @REMOVE_LINE_FOR_BROWSER
8 |
9 |
10 | exports.testRunModule = function (test) {
11 | test.expect(59);
12 | var call_order = [];
13 | var testmodule = {
14 | test1: function (test) {
15 | call_order.push('test1');
16 | test.ok(true, 'ok true');
17 | test.done();
18 | },
19 | test2: function (test) {
20 | call_order.push('test2');
21 | test.ok(false, 'ok false');
22 | test.ok(false, 'ok false');
23 | test.done();
24 | },
25 | test3: function (test) {
26 | call_order.push('test3');
27 | test.done();
28 | }
29 | };
30 | nodeunit.runModule('testmodule', testmodule, {
31 | log: function (assertion) {
32 | call_order.push('log');
33 | },
34 | testStart: function (name) {
35 | call_order.push('testStart');
36 | test.ok(
37 | name.toString() === 'test1' ||
38 | name.toString() === 'test2' ||
39 | name.toString() === 'test3',
40 | 'testStart called with test name '
41 | );
42 | },
43 | testReady: function (tst) {
44 | call_order.push('testReady');
45 | test.ok(tst.done, 'testReady called with non-test object');
46 | test.ok(tst.ok, 'testReady called with non-test object');
47 | test.ok(tst.same, 'testReady called with non-test object');
48 | test.ok(tst.expect, 'testReady called with non-test object');
49 | test.ok(tst._assertion_list, 'testReady called with non-test object');
50 | test.ok(tst.AssertionError, 'testReady called with non-test object');
51 | test.ok(tst.fail, 'testReady called with non-test object');
52 | test.ok(tst.equal, 'testReady called with non-test object');
53 | test.ok(tst.notEqual, 'testReady called with non-test object');
54 | test.ok(tst.deepEqual, 'testReady called with non-test object');
55 | test.ok(tst.notDeepEqual, 'testReady called with non-test object');
56 | test.ok(tst.strictEqual, 'testReady called with non-test object');
57 | test.ok(tst.notStrictEqual, 'testReady called with non-test object');
58 | test.ok(tst.throws, 'testReady called with non-test object');
59 | test.ok(tst.doesNotThrow, 'testReady called with non-test object');
60 | test.ok(tst.ifError, 'testReady called with non-test object');
61 | },
62 | testDone: function (name, assertions) {
63 | call_order.push('testDone');
64 | test.ok(
65 | name.toString() === 'test1' ||
66 | name.toString() === 'test2' ||
67 | name.toString() === 'test3',
68 | 'testDone called with test name'
69 | );
70 | },
71 | moduleDone: function (name, assertions) {
72 | call_order.push('moduleDone');
73 | test.equals(assertions.length, 3);
74 | test.equals(assertions.failures(), 2);
75 | test.equals(name, 'testmodule');
76 | test.ok(typeof assertions.duration === "number");
77 | test.same(call_order, [
78 | 'testStart', 'testReady', 'test1', 'log', 'testDone',
79 | 'testStart', 'testReady', 'test2', 'log', 'log', 'testDone',
80 | 'testStart', 'testReady', 'test3', 'testDone',
81 | 'moduleDone'
82 | ]);
83 | }
84 | }, test.done);
85 | };
86 |
87 |
88 | exports.testRunModuleTestSpec = function (test) {
89 | test.expect(22);
90 | var call_order = [];
91 | var testmodule = {
92 | test1: function (test) {
93 | test.ok(true, 'ok true');
94 | test.done();
95 | },
96 | test2: function (test) {
97 | call_order.push('test2');
98 | test.ok(false, 'ok false');
99 | test.ok(false, 'ok false');
100 | test.done();
101 | },
102 | test3: function (test) {
103 | test.done();
104 | }
105 | };
106 | nodeunit.runModule('testmodule', testmodule, {
107 | testspec: "test2",
108 | log: function (assertion) {
109 | call_order.push('log');
110 | },
111 | testStart: function (name) {
112 | call_order.push('testStart');
113 | test.equals(
114 | name,'test2',
115 | 'testStart called with test name '
116 | );
117 | },
118 | testReady: function (tst) {
119 | call_order.push('testReady');
120 | test.ok(tst.done, 'testReady called with non-test object');
121 | test.ok(tst.ok, 'testReady called with non-test object');
122 | test.ok(tst.same, 'testReady called with non-test object');
123 | test.ok(tst.expect, 'testReady called with non-test object');
124 | test.ok(tst._assertion_list, 'testReady called with non-test object');
125 | test.ok(tst.AssertionError, 'testReady called with non-test object');
126 | test.ok(tst.fail, 'testReady called with non-test object');
127 | test.ok(tst.equal, 'testReady called with non-test object');
128 | test.ok(tst.notEqual, 'testReady called with non-test object');
129 | test.ok(tst.deepEqual, 'testReady called with non-test object');
130 | test.ok(tst.notDeepEqual, 'testReady called with non-test object');
131 | test.ok(tst.strictEqual, 'testReady called with non-test object');
132 | test.ok(tst.notStrictEqual, 'testReady called with non-test object');
133 | test.ok(tst.throws, 'testReady called with non-test object');
134 | test.ok(tst.doesNotThrow, 'testReady called with non-test object');
135 | test.ok(tst.ifError, 'testReady called with non-test object');
136 | },
137 | testDone: function (name, assertions) {
138 | call_order.push('testDone');
139 | test.equal(
140 | name, 'test2',
141 | 'testDone called with test name'
142 | );
143 | },
144 | moduleDone: function (name, assertions) {
145 | call_order.push('moduleDone');
146 | test.equals(assertions.length, 2);
147 | test.equals(name, 'testmodule');
148 | test.ok(typeof assertions.duration === "number");
149 | test.same(call_order, [
150 | 'testStart', 'testReady', 'test2', 'log', 'log', 'testDone',
151 | 'moduleDone'
152 | ]);
153 | }
154 | }, test.done);
155 | };
156 |
157 | exports.testRunModuleEmpty = function (test) {
158 | nodeunit.runModule('module with no exports', {}, {
159 | log: function (assertion) {
160 | test.ok(false, 'log should not be called');
161 | },
162 | testStart: function (name) {
163 | test.ok(false, 'testStart should not be called');
164 | },
165 | testReady: function (tst) {
166 | test.ok(false, 'testReady should not be called');
167 | },
168 | testDone: function (name, assertions) {
169 | test.ok(false, 'testDone should not be called');
170 | },
171 | moduleDone: function (name, assertions) {
172 | test.equals(assertions.length, 0);
173 | test.equals(assertions.failures(), 0);
174 | test.equals(name, 'module with no exports');
175 | test.ok(typeof assertions.duration === "number");
176 | }
177 | }, test.done);
178 | };
179 |
180 |
181 | exports.testNestedTests = function (test) {
182 | var call_order = [];
183 | var m = {
184 | test1: function (test) {
185 | test.done();
186 | },
187 | suite: {
188 | t1: function (test) {
189 | test.done();
190 | },
191 | t2: function (test) {
192 | test.done();
193 | },
194 | another_suite: {
195 | t3: function (test) {
196 | test.done();
197 | }
198 | }
199 | }
200 | };
201 | nodeunit.runModule('modulename', m, {
202 | testStart: function (name) {
203 | call_order.push(['testStart'].concat(name));
204 | },
205 | testReady: function (tst) {
206 | call_order.push(['testReady']);
207 | },
208 | testDone: function (name, assertions) {
209 | call_order.push(['testDone'].concat(name));
210 | }
211 | }, function () {
212 | test.same(call_order, [
213 | ['testStart', 'test1'], ['testReady'], ['testDone', 'test1'],
214 | ['testStart', 'suite', 't1'], ['testReady'], ['testDone', 'suite', 't1'],
215 | ['testStart', 'suite', 't2'], ['testReady'], ['testDone', 'suite', 't2'],
216 | ['testStart', 'suite', 'another_suite', 't3'],
217 | ['testReady'],
218 | ['testDone', 'suite', 'another_suite', 't3']
219 | ]);
220 | test.done();
221 | });
222 | };
223 |
--------------------------------------------------------------------------------
/lib/core.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Nodeunit
3 | * Copyright (c) 2010 Caolan McMahon
4 | * MIT Licensed
5 | *
6 | * THIS FILE SHOULD BE BROWSER-COMPATIBLE JS!
7 | * You can use @REMOVE_LINE_FOR_BROWSER to remove code from the browser build.
8 | * Only code on that line will be removed, it's mostly to avoid requiring code
9 | * that is node specific
10 | */
11 |
12 | /**
13 | * Module dependencies
14 | */
15 |
16 | var async = require('../deps/async'), //@REMOVE_LINE_FOR_BROWSER
17 | nodeunit = require('./nodeunit'), //@REMOVE_LINE_FOR_BROWSER
18 | types = require('./types'); //@REMOVE_LINE_FOR_BROWSER
19 |
20 |
21 | /**
22 | * Added for browser compatibility
23 | */
24 |
25 | var _keys = function (obj) {
26 | if (Object.keys) {
27 | return Object.keys(obj);
28 | }
29 | var keys = [];
30 | for (var k in obj) {
31 | if (obj.hasOwnProperty(k)) {
32 | keys.push(k);
33 | }
34 | }
35 | return keys;
36 | };
37 |
38 |
39 | var _copy = function (obj) {
40 | var nobj = {};
41 | var keys = _keys(obj);
42 | for (var i = 0; i < keys.length; i += 1) {
43 | nobj[keys[i]] = obj[keys[i]];
44 | }
45 | return nobj;
46 | };
47 |
48 |
49 | /**
50 | * Runs a test function (fn) from a loaded module. After the test function
51 | * calls test.done(), the callback is executed with an assertionList as its
52 | * second argument.
53 | *
54 | * @param {String} name
55 | * @param {Function} fn
56 | * @param {Object} opt
57 | * @param {Function} callback
58 | * @api public
59 | */
60 |
61 | exports.runTest = function (name, fn, opt, callback) {
62 | var options = types.options(opt);
63 |
64 | options.testStart(name);
65 | var start = new Date().getTime();
66 | var test = types.test(name, start, options, callback);
67 |
68 | options.testReady(test);
69 | try {
70 | fn(test);
71 | }
72 | catch (e) {
73 | test.done(e);
74 | }
75 | };
76 |
77 | /**
78 | * Takes an object containing test functions or other test suites as properties
79 | * and runs each in series. After all tests have completed, the callback is
80 | * called with a list of all assertions as the second argument.
81 | *
82 | * If a name is passed to this function it is prepended to all test and suite
83 | * names that run within it.
84 | *
85 | * @param {String} name
86 | * @param {Object} suite
87 | * @param {Object} opt
88 | * @param {Function} callback
89 | * @api public
90 | */
91 |
92 | exports.runSuite = function (name, suite, opt, callback) {
93 | suite = wrapGroup(suite);
94 | var keys = _keys(suite);
95 |
96 | async.concatSeries(keys, function (k, cb) {
97 | var prop = suite[k], _name;
98 |
99 | _name = name ? [].concat(name, k) : [k];
100 | _name.toString = function () {
101 | // fallback for old one
102 | return this.join(' - ');
103 | };
104 |
105 | if (typeof prop === 'function') {
106 | var in_name = false,
107 | in_specific_test = (_name.toString() === opt.testFullSpec) ? true : false;
108 | for (var i = 0; i < _name.length; i += 1) {
109 | if (_name[i] === opt.testspec) {
110 | in_name = true;
111 | }
112 | }
113 |
114 | if ((!opt.testFullSpec || in_specific_test) && (!opt.testspec || in_name)) {
115 | if (opt.moduleStart) {
116 | opt.moduleStart();
117 | }
118 | exports.runTest(_name, suite[k], opt, cb);
119 | }
120 | else {
121 | return cb();
122 | }
123 | }
124 | else {
125 | exports.runSuite(_name, suite[k], opt, cb);
126 | }
127 | }, callback);
128 | };
129 |
130 | /**
131 | * Run each exported test function or test suite from a loaded module.
132 | *
133 | * @param {String} name
134 | * @param {Object} mod
135 | * @param {Object} opt
136 | * @param {Function} callback
137 | * @api public
138 | */
139 |
140 | exports.runModule = function (name, mod, opt, callback) {
141 | var options = _copy(types.options(opt));
142 |
143 | var _run = false;
144 | var _moduleStart = options.moduleStart;
145 |
146 | mod = wrapGroup(mod);
147 |
148 | function run_once() {
149 | if (!_run) {
150 | _run = true;
151 | _moduleStart(name);
152 | }
153 | }
154 | options.moduleStart = run_once;
155 |
156 | var start = new Date().getTime();
157 |
158 | exports.runSuite(null, mod, options, function (err, a_list) {
159 | var end = new Date().getTime();
160 | var assertion_list = types.assertionList(a_list, end - start);
161 | options.moduleDone(name, assertion_list);
162 | if (nodeunit.complete) {
163 | nodeunit.complete(name, assertion_list);
164 | }
165 | callback(null, a_list);
166 | });
167 | };
168 |
169 | /**
170 | * Treats an object literal as a list of modules keyed by name. Runs each
171 | * module and finished with calling 'done'. You can think of this as a browser
172 | * safe alternative to runFiles in the nodeunit module.
173 | *
174 | * @param {Object} modules
175 | * @param {Object} opt
176 | * @api public
177 | */
178 |
179 | // TODO: add proper unit tests for this function
180 | exports.runModules = function (modules, opt) {
181 | var all_assertions = [];
182 | var options = types.options(opt);
183 | var start = new Date().getTime();
184 |
185 | async.concatSeries(_keys(modules), function (k, cb) {
186 | exports.runModule(k, modules[k], options, cb);
187 | },
188 | function (err, all_assertions) {
189 | var end = new Date().getTime();
190 | options.done(types.assertionList(all_assertions, end - start));
191 | });
192 | };
193 |
194 |
195 | /**
196 | * Wraps a test function with setUp and tearDown functions.
197 | * Used by testCase.
198 | *
199 | * @param {Function} setUp
200 | * @param {Function} tearDown
201 | * @param {Function} fn
202 | * @api private
203 | */
204 |
205 | var wrapTest = function (setUp, tearDown, fn) {
206 | return function (test) {
207 | var context = {};
208 | if (tearDown) {
209 | var done = test.done;
210 | test.done = function (err) {
211 | try {
212 | tearDown.call(context, function (err2) {
213 | if (err && err2) {
214 | test._assertion_list.push(
215 | types.assertion({error: err})
216 | );
217 | return done(err2);
218 | }
219 | done(err || err2);
220 | });
221 | }
222 | catch (e) {
223 | done(e);
224 | }
225 | };
226 | }
227 | if (setUp) {
228 | setUp.call(context, function (err) {
229 | if (err) {
230 | return test.done(err);
231 | }
232 | fn.call(context, test);
233 | });
234 | }
235 | else {
236 | fn.call(context, test);
237 | }
238 | };
239 | };
240 |
241 |
242 | /**
243 | * Returns a serial callback from two functions.
244 | *
245 | * @param {Function} funcFirst
246 | * @param {Function} funcSecond
247 | * @api private
248 | */
249 |
250 | var getSerialCallback = function (fns) {
251 | if (!fns.length) {
252 | return null;
253 | }
254 | return function (callback) {
255 | var that = this;
256 | var bound_fns = [];
257 | for (var i = 0, len = fns.length; i < len; i++) {
258 | (function (j) {
259 | bound_fns.push(function () {
260 | return fns[j].apply(that, arguments);
261 | });
262 | })(i);
263 | }
264 | return async.series(bound_fns, callback);
265 | };
266 | };
267 |
268 |
269 | /**
270 | * Wraps a group of tests with setUp and tearDown functions.
271 | * Used by testCase.
272 | *
273 | * @param {Object} group
274 | * @param {Array} setUps - parent setUp functions
275 | * @param {Array} tearDowns - parent tearDown functions
276 | * @api private
277 | */
278 |
279 | var wrapGroup = function (group, setUps, tearDowns) {
280 | var tests = {};
281 |
282 | var setUps = setUps ? setUps.slice(): [];
283 | var tearDowns = tearDowns ? tearDowns.slice(): [];
284 |
285 | if (group.setUp) {
286 | setUps.push(group.setUp);
287 | delete group.setUp;
288 | }
289 | if (group.tearDown) {
290 | tearDowns.unshift(group.tearDown);
291 | delete group.tearDown;
292 | }
293 |
294 | var keys = _keys(group);
295 |
296 | for (var i = 0; i < keys.length; i += 1) {
297 | var k = keys[i];
298 | if (typeof group[k] === 'function') {
299 | tests[k] = wrapTest(
300 | getSerialCallback(setUps),
301 | getSerialCallback(tearDowns),
302 | group[k]
303 | );
304 | }
305 | else if (typeof group[k] === 'object') {
306 | tests[k] = wrapGroup(group[k], setUps, tearDowns);
307 | }
308 | }
309 | return tests;
310 | };
311 |
312 |
313 | /**
314 | * Backwards compatibility for test suites using old testCase API
315 | */
316 |
317 | exports.testCase = function (suite) {
318 | return suite;
319 | };
320 |
--------------------------------------------------------------------------------
/lib/assert.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is based on the node.js assert module, but with some small
3 | * changes for browser-compatibility
4 | * THIS FILE SHOULD BE BROWSER-COMPATIBLE JS!
5 | */
6 |
7 |
8 | /**
9 | * Added for browser compatibility
10 | */
11 |
12 | var _keys = function(obj){
13 | if(Object.keys) return Object.keys(obj);
14 | if (typeof obj != 'object' && typeof obj != 'function') {
15 | throw new TypeError('-');
16 | }
17 | var keys = [];
18 | for(var k in obj){
19 | if(obj.hasOwnProperty(k)) keys.push(k);
20 | }
21 | return keys;
22 | };
23 |
24 |
25 |
26 | // http://wiki.commonjs.org/wiki/Unit_Testing/1.0
27 | //
28 | // THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8!
29 | //
30 | // Originally from narwhal.js (http://narwhaljs.org)
31 | // Copyright (c) 2009 Thomas Robinson <280north.com>
32 | //
33 | // Permission is hereby granted, free of charge, to any person obtaining a copy
34 | // of this software and associated documentation files (the 'Software'), to
35 | // deal in the Software without restriction, including without limitation the
36 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
37 | // sell copies of the Software, and to permit persons to whom the Software is
38 | // furnished to do so, subject to the following conditions:
39 | //
40 | // The above copyright notice and this permission notice shall be included in
41 | // all copies or substantial portions of the Software.
42 | //
43 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
44 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
45 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
46 | // AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
47 | // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
48 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
49 |
50 |
51 | var pSlice = Array.prototype.slice;
52 |
53 | // 1. The assert module provides functions that throw
54 | // AssertionError's when particular conditions are not met. The
55 | // assert module must conform to the following interface.
56 |
57 | var assert = exports;
58 |
59 | // 2. The AssertionError is defined in assert.
60 | // new assert.AssertionError({message: message, actual: actual, expected: expected})
61 |
62 | assert.AssertionError = function AssertionError (options) {
63 | this.name = "AssertionError";
64 | this.message = options.message;
65 | this.actual = options.actual;
66 | this.expected = options.expected;
67 | this.operator = options.operator;
68 | var stackStartFunction = options.stackStartFunction || fail;
69 |
70 | if (Error.captureStackTrace) {
71 | Error.captureStackTrace(this, stackStartFunction);
72 | }
73 | };
74 | // code from util.inherits in node
75 | assert.AssertionError.super_ = Error;
76 |
77 |
78 | // EDITED FOR BROWSER COMPATIBILITY: replaced Object.create call
79 | // TODO: test what effect this may have
80 | var ctor = function () { this.constructor = assert.AssertionError; };
81 | ctor.prototype = Error.prototype;
82 | assert.AssertionError.prototype = new ctor();
83 |
84 |
85 | assert.AssertionError.prototype.toString = function() {
86 | if (this.message) {
87 | return [this.name+":", this.message].join(' ');
88 | } else {
89 | return [ this.name+":"
90 | , typeof this.expected !== 'undefined' ? JSON.stringify(this.expected) : 'undefined'
91 | , this.operator
92 | , typeof this.actual !== 'undefined' ? JSON.stringify(this.actual) : 'undefined'
93 | ].join(" ");
94 | }
95 | };
96 |
97 | // assert.AssertionError instanceof Error
98 |
99 | assert.AssertionError.__proto__ = Error.prototype;
100 |
101 | // At present only the three keys mentioned above are used and
102 | // understood by the spec. Implementations or sub modules can pass
103 | // other keys to the AssertionError's constructor - they will be
104 | // ignored.
105 |
106 | // 3. All of the following functions must throw an AssertionError
107 | // when a corresponding condition is not met, with a message that
108 | // may be undefined if not provided. All assertion methods provide
109 | // both the actual and expected values to the assertion error for
110 | // display purposes.
111 |
112 | function fail(actual, expected, message, operator, stackStartFunction) {
113 | throw new assert.AssertionError({
114 | message: message,
115 | actual: actual,
116 | expected: expected,
117 | operator: operator,
118 | stackStartFunction: stackStartFunction
119 | });
120 | }
121 |
122 | // EXTENSION! allows for well behaved errors defined elsewhere.
123 | assert.fail = fail;
124 |
125 | // 4. Pure assertion tests whether a value is truthy, as determined
126 | // by !!guard.
127 | // assert.ok(guard, message_opt);
128 | // This statement is equivalent to assert.equal(true, guard,
129 | // message_opt);. To test strictly for the value true, use
130 | // assert.strictEqual(true, guard, message_opt);.
131 |
132 | assert.ok = function ok(value, message) {
133 | if (!!!value) fail(value, true, message, "==", assert.ok);
134 | };
135 |
136 | // 5. The equality assertion tests shallow, coercive equality with
137 | // ==.
138 | // assert.equal(actual, expected, message_opt);
139 |
140 | assert.equal = function equal(actual, expected, message) {
141 | if (actual != expected) fail(actual, expected, message, "==", assert.equal);
142 | };
143 |
144 | // 6. The non-equality assertion tests for whether two objects are not equal
145 | // with != assert.notEqual(actual, expected, message_opt);
146 |
147 | assert.notEqual = function notEqual(actual, expected, message) {
148 | if (actual == expected) {
149 | fail(actual, expected, message, "!=", assert.notEqual);
150 | }
151 | };
152 |
153 | // 7. The equivalence assertion tests a deep equality relation.
154 | // assert.deepEqual(actual, expected, message_opt);
155 |
156 | assert.deepEqual = function deepEqual(actual, expected, message) {
157 | if (!_deepEqual(actual, expected)) {
158 | fail(actual, expected, message, "deepEqual", assert.deepEqual);
159 | }
160 | };
161 |
162 | var Buffer = null;
163 | if (typeof require !== 'undefined' && typeof process !== 'undefined') {
164 | try {
165 | Buffer = require('buffer').Buffer;
166 | }
167 | catch (e) {
168 | // May be a CommonJS environment other than Node.js
169 | Buffer = null;
170 | }
171 | }
172 |
173 | function _deepEqual(actual, expected) {
174 | // 7.1. All identical values are equivalent, as determined by ===.
175 | if (actual === expected)
176 | return true;
177 |
178 | // Convert to primitives, if supported
179 | actual = actual.valueOf ? actual.valueOf() : actual;
180 | expected = expected.valueOf ? expected.valueOf() : expected;
181 |
182 | // 7.2. If the expected value is a Date object, the actual value is
183 | // equivalent if it is also a Date object that refers to the same time.
184 | if (actual instanceof Date && expected instanceof Date) {
185 | return actual.getTime() === expected.getTime();
186 |
187 | // 7.2.1 If the expcted value is a RegExp object, the actual value is
188 | // equivalent if it is also a RegExp object that refers to the same source and options
189 | } else if (actual instanceof RegExp && expected instanceof RegExp) {
190 | return actual.source === expected.source &&
191 | actual.global === expected.global &&
192 | actual.ignoreCase === expected.ignoreCase &&
193 | actual.multiline === expected.multiline;
194 |
195 | } else if (Buffer && actual instanceof Buffer && expected instanceof Buffer) {
196 | return (function() {
197 | var i, len;
198 |
199 | for (i = 0, len = expected.length; i < len; i++) {
200 | if (actual[i] !== expected[i]) {
201 | return false;
202 | }
203 | }
204 | return actual.length === expected.length;
205 | })();
206 | // 7.3. Other pairs that do not both pass typeof value == "object",
207 | // equivalence is determined by ==.
208 | } else if (typeof actual != 'object' && typeof expected != 'object') {
209 | return actual == expected;
210 |
211 | // 7.4. For all other Object pairs, including Array objects, equivalence is
212 | // determined by having the same number of owned properties (as verified
213 | // with Object.prototype.hasOwnProperty.call), the same set of keys
214 | // (although not necessarily the same order), equivalent values for every
215 | // corresponding key, and an identical "prototype" property. Note: this
216 | // accounts for both named and indexed properties on Arrays.
217 | } else {
218 | return objEquiv(actual, expected);
219 | }
220 | }
221 |
222 | function isUndefinedOrNull (value) {
223 | return value === null || value === undefined;
224 | }
225 |
226 | function isArguments (object) {
227 | return Object.prototype.toString.call(object) == '[object Arguments]';
228 | }
229 |
230 | function objEquiv (a, b) {
231 | if (isUndefinedOrNull(a) || isUndefinedOrNull(b))
232 | return false;
233 |
234 | // an identical "prototype" property.
235 | if (a.prototype !== b.prototype) return false;
236 | //~~~I've managed to break Object.keys through screwy arguments passing.
237 | // Converting to array solves the problem.
238 | if (isArguments(a)) {
239 | if (!isArguments(b)) {
240 | return false;
241 | }
242 | a = pSlice.call(a);
243 | b = pSlice.call(b);
244 | return _deepEqual(a, b);
245 | }
246 | try{
247 | var ka = _keys(a),
248 | kb = _keys(b),
249 | key, i;
250 | } catch (e) {//happens when one is a string literal and the other isn't
251 | return false;
252 | }
253 | // having the same number of owned properties (keys incorporates hasOwnProperty)
254 | if (ka.length != kb.length)
255 | return false;
256 | //the same set of keys (although not necessarily the same order),
257 | ka.sort();
258 | kb.sort();
259 | //~~~cheap key test
260 | for (i = ka.length - 1; i >= 0; i--) {
261 | if (ka[i] != kb[i])
262 | return false;
263 | }
264 | //equivalent values for every corresponding key, and
265 | //~~~possibly expensive deep test
266 | for (i = ka.length - 1; i >= 0; i--) {
267 | key = ka[i];
268 | if (!_deepEqual(a[key], b[key] ))
269 | return false;
270 | }
271 | return true;
272 | }
273 |
274 | // 8. The non-equivalence assertion tests for any deep inequality.
275 | // assert.notDeepEqual(actual, expected, message_opt);
276 |
277 | assert.notDeepEqual = function notDeepEqual(actual, expected, message) {
278 | if (_deepEqual(actual, expected)) {
279 | fail(actual, expected, message, "notDeepEqual", assert.notDeepEqual);
280 | }
281 | };
282 |
283 | // 9. The strict equality assertion tests strict equality, as determined by ===.
284 | // assert.strictEqual(actual, expected, message_opt);
285 |
286 | assert.strictEqual = function strictEqual(actual, expected, message) {
287 | if (actual !== expected) {
288 | fail(actual, expected, message, "===", assert.strictEqual);
289 | }
290 | };
291 |
292 | // 10. The strict non-equality assertion tests for strict inequality, as determined by !==.
293 | // assert.notStrictEqual(actual, expected, message_opt);
294 |
295 | assert.notStrictEqual = function notStrictEqual(actual, expected, message) {
296 | if (actual === expected) {
297 | fail(actual, expected, message, "!==", assert.notStrictEqual);
298 | }
299 | };
300 |
301 | function expectedException(actual, expected) {
302 | if (!actual || !expected) {
303 | return false;
304 | }
305 |
306 | if (expected instanceof RegExp) {
307 | return expected.test(actual.message || actual);
308 | } else if (actual instanceof expected) {
309 | return true;
310 | } else if (expected.call({}, actual) === true) {
311 | return true;
312 | }
313 |
314 | return false;
315 | }
316 |
317 | function _throws(shouldThrow, block, expected, message) {
318 | var actual;
319 |
320 | if (typeof expected === 'string') {
321 | message = expected;
322 | expected = null;
323 | }
324 |
325 | try {
326 | block();
327 | } catch (e) {
328 | actual = e;
329 | }
330 |
331 | message = (expected && expected.name ? ' (' + expected.name + ').' : '.') +
332 | (message ? ' ' + message : '.');
333 |
334 | if (shouldThrow && !actual) {
335 | fail('Missing expected exception' + message);
336 | }
337 |
338 | if (!shouldThrow && expectedException(actual, expected)) {
339 | fail('Got unwanted exception' + message);
340 | }
341 |
342 | if ((shouldThrow && actual && expected &&
343 | !expectedException(actual, expected)) || (!shouldThrow && actual)) {
344 | throw actual;
345 | }
346 | }
347 |
348 | // 11. Expected to throw an error:
349 | // assert.throws(block, Error_opt, message_opt);
350 |
351 | assert.throws = function(block, /*optional*/error, /*optional*/message) {
352 | _throws.apply(this, [true].concat(pSlice.call(arguments)));
353 | };
354 |
355 | // EXTENSION! This is annoying to write outside this module.
356 | assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) {
357 | _throws.apply(this, [false].concat(pSlice.call(arguments)));
358 | };
359 |
360 | assert.ifError = function (err) { if (err) {throw err;}};
361 |
--------------------------------------------------------------------------------
/deps/json2.js:
--------------------------------------------------------------------------------
1 | /*
2 | http://www.JSON.org/json2.js
3 | 2010-11-17
4 |
5 | Public Domain.
6 |
7 | NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
8 |
9 | See http://www.JSON.org/js.html
10 |
11 |
12 | This code should be minified before deployment.
13 | See http://javascript.crockford.com/jsmin.html
14 |
15 | USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
16 | NOT CONTROL.
17 |
18 |
19 | This file creates a global JSON object containing two methods: stringify
20 | and parse.
21 |
22 | JSON.stringify(value, replacer, space)
23 | value any JavaScript value, usually an object or array.
24 |
25 | replacer an optional parameter that determines how object
26 | values are stringified for objects. It can be a
27 | function or an array of strings.
28 |
29 | space an optional parameter that specifies the indentation
30 | of nested structures. If it is omitted, the text will
31 | be packed without extra whitespace. If it is a number,
32 | it will specify the number of spaces to indent at each
33 | level. If it is a string (such as '\t' or ' '),
34 | it contains the characters used to indent at each level.
35 |
36 | This method produces a JSON text from a JavaScript value.
37 |
38 | When an object value is found, if the object contains a toJSON
39 | method, its toJSON method will be called and the result will be
40 | stringified. A toJSON method does not serialize: it returns the
41 | value represented by the name/value pair that should be serialized,
42 | or undefined if nothing should be serialized. The toJSON method
43 | will be passed the key associated with the value, and this will be
44 | bound to the value
45 |
46 | For example, this would serialize Dates as ISO strings.
47 |
48 | Date.prototype.toJSON = function (key) {
49 | function f(n) {
50 | // Format integers to have at least two digits.
51 | return n < 10 ? '0' + n : n;
52 | }
53 |
54 | return this.getUTCFullYear() + '-' +
55 | f(this.getUTCMonth() + 1) + '-' +
56 | f(this.getUTCDate()) + 'T' +
57 | f(this.getUTCHours()) + ':' +
58 | f(this.getUTCMinutes()) + ':' +
59 | f(this.getUTCSeconds()) + 'Z';
60 | };
61 |
62 | You can provide an optional replacer method. It will be passed the
63 | key and value of each member, with this bound to the containing
64 | object. The value that is returned from your method will be
65 | serialized. If your method returns undefined, then the member will
66 | be excluded from the serialization.
67 |
68 | If the replacer parameter is an array of strings, then it will be
69 | used to select the members to be serialized. It filters the results
70 | such that only members with keys listed in the replacer array are
71 | stringified.
72 |
73 | Values that do not have JSON representations, such as undefined or
74 | functions, will not be serialized. Such values in objects will be
75 | dropped; in arrays they will be replaced with null. You can use
76 | a replacer function to replace those with JSON values.
77 | JSON.stringify(undefined) returns undefined.
78 |
79 | The optional space parameter produces a stringification of the
80 | value that is filled with line breaks and indentation to make it
81 | easier to read.
82 |
83 | If the space parameter is a non-empty string, then that string will
84 | be used for indentation. If the space parameter is a number, then
85 | the indentation will be that many spaces.
86 |
87 | Example:
88 |
89 | text = JSON.stringify(['e', {pluribus: 'unum'}]);
90 | // text is '["e",{"pluribus":"unum"}]'
91 |
92 |
93 | text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
94 | // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
95 |
96 | text = JSON.stringify([new Date()], function (key, value) {
97 | return this[key] instanceof Date ?
98 | 'Date(' + this[key] + ')' : value;
99 | });
100 | // text is '["Date(---current time---)"]'
101 |
102 |
103 | JSON.parse(text, reviver)
104 | This method parses a JSON text to produce an object or array.
105 | It can throw a SyntaxError exception.
106 |
107 | The optional reviver parameter is a function that can filter and
108 | transform the results. It receives each of the keys and values,
109 | and its return value is used instead of the original value.
110 | If it returns what it received, then the structure is not modified.
111 | If it returns undefined then the member is deleted.
112 |
113 | Example:
114 |
115 | // Parse the text. Values that look like ISO date strings will
116 | // be converted to Date objects.
117 |
118 | myData = JSON.parse(text, function (key, value) {
119 | var a;
120 | if (typeof value === 'string') {
121 | a =
122 | /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
123 | if (a) {
124 | return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
125 | +a[5], +a[6]));
126 | }
127 | }
128 | return value;
129 | });
130 |
131 | myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
132 | var d;
133 | if (typeof value === 'string' &&
134 | value.slice(0, 5) === 'Date(' &&
135 | value.slice(-1) === ')') {
136 | d = new Date(value.slice(5, -1));
137 | if (d) {
138 | return d;
139 | }
140 | }
141 | return value;
142 | });
143 |
144 |
145 | This is a reference implementation. You are free to copy, modify, or
146 | redistribute.
147 | */
148 |
149 | /*jslint evil: true, strict: false, regexp: false */
150 |
151 | /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
152 | call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
153 | getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
154 | lastIndex, length, parse, prototype, push, replace, slice, stringify,
155 | test, toJSON, toString, valueOf
156 | */
157 |
158 |
159 | // Create a JSON object only if one does not already exist. We create the
160 | // methods in a closure to avoid creating global variables.
161 |
162 | var JSON = {};
163 |
164 | (function () {
165 | "use strict";
166 |
167 | function f(n) {
168 | // Format integers to have at least two digits.
169 | return n < 10 ? '0' + n : n;
170 | }
171 |
172 | if (typeof Date.prototype.toJSON !== 'function') {
173 |
174 | Date.prototype.toJSON = function (key) {
175 |
176 | return isFinite(this.valueOf()) ?
177 | this.getUTCFullYear() + '-' +
178 | f(this.getUTCMonth() + 1) + '-' +
179 | f(this.getUTCDate()) + 'T' +
180 | f(this.getUTCHours()) + ':' +
181 | f(this.getUTCMinutes()) + ':' +
182 | f(this.getUTCSeconds()) + 'Z' : null;
183 | };
184 |
185 | String.prototype.toJSON =
186 | Number.prototype.toJSON =
187 | Boolean.prototype.toJSON = function (key) {
188 | return this.valueOf();
189 | };
190 | }
191 |
192 | var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
193 | escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
194 | gap,
195 | indent,
196 | meta = { // table of character substitutions
197 | '\b': '\\b',
198 | '\t': '\\t',
199 | '\n': '\\n',
200 | '\f': '\\f',
201 | '\r': '\\r',
202 | '"' : '\\"',
203 | '\\': '\\\\'
204 | },
205 | rep;
206 |
207 |
208 | function quote(string) {
209 |
210 | // If the string contains no control characters, no quote characters, and no
211 | // backslash characters, then we can safely slap some quotes around it.
212 | // Otherwise we must also replace the offending characters with safe escape
213 | // sequences.
214 |
215 | escapable.lastIndex = 0;
216 | return escapable.test(string) ?
217 | '"' + string.replace(escapable, function (a) {
218 | var c = meta[a];
219 | return typeof c === 'string' ? c :
220 | '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
221 | }) + '"' :
222 | '"' + string + '"';
223 | }
224 |
225 |
226 | function str(key, holder) {
227 |
228 | // Produce a string from holder[key].
229 |
230 | var i, // The loop counter.
231 | k, // The member key.
232 | v, // The member value.
233 | length,
234 | mind = gap,
235 | partial,
236 | value = holder[key];
237 |
238 | // If the value has a toJSON method, call it to obtain a replacement value.
239 |
240 | if (value && typeof value === 'object' &&
241 | typeof value.toJSON === 'function') {
242 | value = value.toJSON(key);
243 | }
244 |
245 | // If we were called with a replacer function, then call the replacer to
246 | // obtain a replacement value.
247 |
248 | if (typeof rep === 'function') {
249 | value = rep.call(holder, key, value);
250 | }
251 |
252 | // What happens next depends on the value's type.
253 |
254 | switch (typeof value) {
255 | case 'string':
256 | return quote(value);
257 |
258 | case 'number':
259 |
260 | // JSON numbers must be finite. Encode non-finite numbers as null.
261 |
262 | return isFinite(value) ? String(value) : 'null';
263 |
264 | case 'boolean':
265 | case 'null':
266 |
267 | // If the value is a boolean or null, convert it to a string. Note:
268 | // typeof null does not produce 'null'. The case is included here in
269 | // the remote chance that this gets fixed someday.
270 |
271 | return String(value);
272 |
273 | // If the type is 'object', we might be dealing with an object or an array or
274 | // null.
275 |
276 | case 'object':
277 |
278 | // Due to a specification blunder in ECMAScript, typeof null is 'object',
279 | // so watch out for that case.
280 |
281 | if (!value) {
282 | return 'null';
283 | }
284 |
285 | // Make an array to hold the partial results of stringifying this object value.
286 |
287 | gap += indent;
288 | partial = [];
289 |
290 | // Is the value an array?
291 |
292 | if (Object.prototype.toString.apply(value) === '[object Array]') {
293 |
294 | // The value is an array. Stringify every element. Use null as a placeholder
295 | // for non-JSON values.
296 |
297 | length = value.length;
298 | for (i = 0; i < length; i += 1) {
299 | partial[i] = str(i, value) || 'null';
300 | }
301 |
302 | // Join all of the elements together, separated with commas, and wrap them in
303 | // brackets.
304 |
305 | v = partial.length === 0 ? '[]' :
306 | gap ? '[\n' + gap +
307 | partial.join(',\n' + gap) + '\n' +
308 | mind + ']' :
309 | '[' + partial.join(',') + ']';
310 | gap = mind;
311 | return v;
312 | }
313 |
314 | // If the replacer is an array, use it to select the members to be stringified.
315 |
316 | if (rep && typeof rep === 'object') {
317 | length = rep.length;
318 | for (i = 0; i < length; i += 1) {
319 | k = rep[i];
320 | if (typeof k === 'string') {
321 | v = str(k, value);
322 | if (v) {
323 | partial.push(quote(k) + (gap ? ': ' : ':') + v);
324 | }
325 | }
326 | }
327 | } else {
328 |
329 | // Otherwise, iterate through all of the keys in the object.
330 |
331 | for (k in value) {
332 | if (Object.hasOwnProperty.call(value, k)) {
333 | v = str(k, value);
334 | if (v) {
335 | partial.push(quote(k) + (gap ? ': ' : ':') + v);
336 | }
337 | }
338 | }
339 | }
340 |
341 | // Join all of the member texts together, separated with commas,
342 | // and wrap them in braces.
343 |
344 | v = partial.length === 0 ? '{}' :
345 | gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
346 | mind + '}' : '{' + partial.join(',') + '}';
347 | gap = mind;
348 | return v;
349 | }
350 | }
351 |
352 | // If the JSON object does not yet have a stringify method, give it one.
353 |
354 | if (typeof JSON.stringify !== 'function') {
355 | JSON.stringify = function (value, replacer, space) {
356 |
357 | // The stringify method takes a value and an optional replacer, and an optional
358 | // space parameter, and returns a JSON text. The replacer can be a function
359 | // that can replace values, or an array of strings that will select the keys.
360 | // A default replacer method can be provided. Use of the space parameter can
361 | // produce text that is more easily readable.
362 |
363 | var i;
364 | gap = '';
365 | indent = '';
366 |
367 | // If the space parameter is a number, make an indent string containing that
368 | // many spaces.
369 |
370 | if (typeof space === 'number') {
371 | for (i = 0; i < space; i += 1) {
372 | indent += ' ';
373 | }
374 |
375 | // If the space parameter is a string, it will be used as the indent string.
376 |
377 | } else if (typeof space === 'string') {
378 | indent = space;
379 | }
380 |
381 | // If there is a replacer, it must be a function or an array.
382 | // Otherwise, throw an error.
383 |
384 | rep = replacer;
385 | if (replacer && typeof replacer !== 'function' &&
386 | (typeof replacer !== 'object' ||
387 | typeof replacer.length !== 'number')) {
388 | throw new Error('JSON.stringify');
389 | }
390 |
391 | // Make a fake root object containing our value under the key of ''.
392 | // Return the result of stringifying the value.
393 |
394 | return str('', {'': value});
395 | };
396 | }
397 |
398 |
399 | // If the JSON object does not yet have a parse method, give it one.
400 |
401 | if (typeof JSON.parse !== 'function') {
402 | JSON.parse = function (text, reviver) {
403 |
404 | // The parse method takes a text and an optional reviver function, and returns
405 | // a JavaScript value if the text is a valid JSON text.
406 |
407 | var j;
408 |
409 | function walk(holder, key) {
410 |
411 | // The walk method is used to recursively walk the resulting structure so
412 | // that modifications can be made.
413 |
414 | var k, v, value = holder[key];
415 | if (value && typeof value === 'object') {
416 | for (k in value) {
417 | if (Object.hasOwnProperty.call(value, k)) {
418 | v = walk(value, k);
419 | if (v !== undefined) {
420 | value[k] = v;
421 | } else {
422 | delete value[k];
423 | }
424 | }
425 | }
426 | }
427 | return reviver.call(holder, key, value);
428 | }
429 |
430 |
431 | // Parsing happens in four stages. In the first stage, we replace certain
432 | // Unicode characters with escape sequences. JavaScript handles many characters
433 | // incorrectly, either silently deleting them, or treating them as line endings.
434 |
435 | text = String(text);
436 | cx.lastIndex = 0;
437 | if (cx.test(text)) {
438 | text = text.replace(cx, function (a) {
439 | return '\\u' +
440 | ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
441 | });
442 | }
443 |
444 | // In the second stage, we run the text against regular expressions that look
445 | // for non-JSON patterns. We are especially concerned with '()' and 'new'
446 | // because they can cause invocation, and '=' because it can cause mutation.
447 | // But just to be safe, we want to reject all unexpected forms.
448 |
449 | // We split the second stage into 4 regexp operations in order to work around
450 | // crippling inefficiencies in IE's and Safari's regexp engines. First we
451 | // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
452 | // replace all simple value tokens with ']' characters. Third, we delete all
453 | // open brackets that follow a colon or comma or that begin the text. Finally,
454 | // we look to see that the remaining characters are only whitespace or ']' or
455 | // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
456 |
457 | if (/^[\],:{}\s]*$/
458 | .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
459 | .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
460 | .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
461 |
462 | // In the third stage we use the eval function to compile the text into a
463 | // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
464 | // in JavaScript: it can begin a block or an object literal. We wrap the text
465 | // in parens to eliminate the ambiguity.
466 |
467 | j = eval('(' + text + ')');
468 |
469 | // In the optional fourth stage, we recursively walk the new structure, passing
470 | // each name/value pair to a reviver function for possible transformation.
471 |
472 | return typeof reviver === 'function' ?
473 | walk({'': j}, '') : j;
474 | }
475 |
476 | // If the text is not JSON parseable, then a SyntaxError is thrown.
477 |
478 | throw new SyntaxError('JSON.parse');
479 | };
480 | }
481 | }());
482 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Nodeunit
2 | ========
3 |
4 | [](https://travis-ci.org/caolan/nodeunit)
5 |
6 | Simple syntax, powerful tools. Nodeunit provides easy async unit testing for
7 | node.js and the browser.
8 |
9 |
10 | DEPRECATED PROJECT
11 | -----
12 |
13 | The project is very stale. We've kept it working on new versions of node, and sometimes merged small PRs that help teams relying on nodeunit.
14 |
15 | Nodeunit was the arguably first testing framework developed for node. It was very useful at the time, but there's an overwhelming number
16 | of other worthwhile testing solutions out there that are actively maintained. tap, ava, tape, mocha, jasmine, jest, ... the list goes on and on.
17 |
18 | If Nodeunit were truly bringing some different philosophy to the testing scene I'd say yes effort should be made to shore up it's development,
19 | but given how many other great options there are out there, a benefit of letting it atrophy is it's one less choice people have to make when
20 | choosing a testing solution. *You are strongly encouraged to check out other more modern testing options.*
21 |
22 |
23 | Features
24 | --------
25 |
26 | * Simple to use
27 | * Just export the tests from a module
28 | * Works with node.js and in the browser
29 | * Helps you avoid common pitfalls when testing asynchronous code
30 | * Easy to add test cases with setUp and tearDown functions if you wish
31 | * Flexible reporters for custom output, built-in support for HTML and jUnit XML
32 | * Allows the use of mocks and stubs
33 |
34 | Contributors
35 | ------------
36 |
37 | * [alexgorbatchev](https://github.com/alexgorbatchev)
38 | * [alexkwolfe](https://github.com/alexkwolfe)
39 | * [azatoth](https://github.com/azatoth)
40 | * [kadirpekel](https://github.com/kadirpekel)
41 | * [lambdalisue](https://github.com/lambdalisue)
42 | * [luebken](https://github.com/luebken)
43 | * [orlandov](https://github.com/orlandov)
44 | * [Sannis](https://github.com/Sannis)
45 | * [sstephenson](https://github.com/sstephenson)
46 | * [thegreatape](https://github.com/thegreatape)
47 | * [mmalecki](https://github.com/mmalecki)
48 | * and thanks to [cjohansen](https://github.com/cjohansen) for input and advice
49 | on implementing setUp and tearDown functions. See
50 | [cjohansen's fork](https://github.com/cjohansen/nodeunit).
51 |
52 | Also, check out gerad's [nodeunit-dsl](https://github.com/gerad/nodeunit-dsl)
53 | project, which implements a 'pretty dsl on top of nodeunit'.
54 |
55 | More contributor information can be found in the
56 | [CONTRIBUTORS.md](https://github.com/caolan/nodeunit/blob/master/CONTRIBUTORS.md)
57 | file.
58 |
59 | Usage
60 | -----
61 |
62 | Here is an example unit test module:
63 |
64 | exports.testSomething = function(test) {
65 | test.expect(1);
66 | test.ok(true, "this assertion should pass");
67 | test.done();
68 | };
69 |
70 | exports.testSomethingElse = function(test) {
71 | test.ok(false, "this assertion should fail");
72 | test.done();
73 | };
74 |
75 | When run using the included test runner, this will output the following:
76 |
77 |
78 |
79 | Installation
80 | ------------
81 |
82 | There are two options for installing nodeunit:
83 |
84 | 1. Clone / download nodeunit from [github](https://github.com/caolan/nodeunit),
85 | then:
86 |
87 | make && sudo make install
88 |
89 | 2. Install via npm:
90 |
91 | npm install nodeunit -g
92 |
93 | API Documentation
94 | -----------------
95 |
96 | Nodeunit uses the functions available in the node.js
97 | [assert module](http://nodejs.org/docs/v0.4.2/api/assert.html):
98 |
99 | * __ok(value, [message])__ - Tests if value is a true value.
100 | * __equal(actual, expected, [message])__ - Tests shallow, coercive equality
101 | with the equal comparison operator ( == ).
102 | * __notEqual(actual, expected, [message])__ - Tests shallow, coercive
103 | non-equality with the not equal comparison operator ( != ).
104 | * __deepEqual(actual, expected, [message])__ - Tests for deep equality.
105 | * __notDeepEqual(actual, expected, [message])__ - Tests for any deep
106 | inequality.
107 | * __strictEqual(actual, expected, [message])__ - Tests strict equality, as
108 | determined by the strict equality operator ( === )
109 | * __notStrictEqual(actual, expected, [message])__ - Tests strict non-equality,
110 | as determined by the strict not equal operator ( !== )
111 | * __throws(block, [error], [message])__ - Expects block to throw an error.
112 | * __doesNotThrow(block, [error], [message])__ - Expects block not to throw an
113 | error.
114 | * __ifError(value)__ - Tests if value is not a false value, throws if it is a
115 | true value. Useful when testing the first argument, error in callbacks.
116 |
117 | Nodeunit also provides the following functions within tests:
118 |
119 | * __expect(amount)__ - Specify how many assertions are expected to run within a
120 | test. Very useful for ensuring that all your callbacks and assertions are
121 | run.
122 | * __done()__ - Finish the current test function, and move on to the next. ALL
123 | tests should call this!
124 |
125 | Nodeunit aims to be simple and easy to learn. This is achieved through using
126 | existing structures (such as node.js modules) to maximum effect, and reducing
127 | the API where possible, to make it easier to digest.
128 |
129 | Tests are simply exported from a module, but they are still run in the order
130 | they are defined.
131 |
132 | __Note:__ Users of old nodeunit versions may remember using `ok`, `equals` and
133 | `same` in the style of qunit, instead of the assert functions above. These
134 | functions still exist for backwards compatibility, and are simply aliases to
135 | their assert module counterparts.
136 |
137 |
138 | Asynchronous Testing
139 | --------------------
140 |
141 | When testing asynchronous code, there are a number of sharp edges to watch out
142 | for. Thankfully, nodeunit is designed to help you avoid as many of these
143 | pitfalls as possible. For the most part, testing asynchronous code in nodeunit
144 | _just works_.
145 |
146 |
147 | ### Tests run in series
148 |
149 | While running tests in parallel seems like a good idea for speeding up your
150 | test suite, in practice I've found it means writing much more complicated
151 | tests. Because of node's module cache, running tests in parallel means mocking
152 | and stubbing is pretty much impossible. One of the nicest things about testing
153 | in javascript is the ease of doing stubs:
154 |
155 | var _readFile = fs.readFile;
156 | fs.readFile = function(path, callback) {
157 | // it's a stub!
158 | };
159 | // test function that uses fs.readFile
160 |
161 | // we're done
162 | fs.readFile = _readFile;
163 |
164 | You cannot do this when running tests in parallel. In order to keep testing as
165 | simple as possible, nodeunit avoids it. Thankfully, most unit-test suites run
166 | fast anyway.
167 |
168 |
169 | ### Explicit ending of tests
170 |
171 | When testing async code it's important that tests end at the correct point, not
172 | just after a given number of assertions. Otherwise your tests can run short,
173 | ending before all assertions have completed. It's important to detect too
174 | many assertions as well as too few. Combining explicit ending of tests with
175 | an expected number of assertions helps to avoid false test passes, so be sure
176 | to use the `test.expect()` method at the start of your test functions, and
177 | `test.done()` when finished.
178 |
179 |
180 | Groups, setUp and tearDown
181 | --------------------------
182 |
183 | Nodeunit allows the nesting of test functions:
184 |
185 | exports.test1 = function (test) {
186 | ...
187 | }
188 |
189 | exports.group = {
190 | test2: function (test) {
191 | ...
192 | },
193 | test3: function (test) {
194 | ...
195 | }
196 | }
197 |
198 | This would be run as:
199 |
200 | test1
201 | group - test2
202 | group - test3
203 |
204 | Using these groups, Nodeunit allows you to define a `setUp` function, which is
205 | run before each test, and a `tearDown` function, which is run after each test
206 | calls `test.done()`:
207 |
208 | module.exports = {
209 | setUp: function (callback) {
210 | this.foo = 'bar';
211 | callback();
212 | },
213 | tearDown: function (callback) {
214 | // clean up
215 | callback();
216 | },
217 | test1: function (test) {
218 | test.equals(this.foo, 'bar');
219 | test.done();
220 | }
221 | };
222 |
223 | In this way, it's possible to have multiple groups of tests in a module, each
224 | group with its own setUp and tearDown functions.
225 |
226 |
227 | Running Tests
228 | -------------
229 |
230 | Nodeunit comes with a basic command-line test runner, which can be installed
231 | using `sudo make install`. Example usage:
232 |
233 | nodeunit testmodule1.js testfolder [...]
234 |
235 | If no entry file specified, `test` defaults.
236 |
237 | The default test reporter uses color output, because I think that's more fun :) I
238 | intend to add a no-color option in future. To give you a feeling of the fun you'll
239 | be having writing tests, lets fix the example at the start of the README:
240 |
241 |
242 |
243 | Ahhh, Doesn't that feel better?
244 |
245 | When using the included test runner, it will exit using the failed number of
246 | assertions as the exit code. This means it exits with 0 when all tests pass.
247 |
248 |
249 | ### Command-line Options
250 |
251 | * __--reporter FILE__ - you can set the test reporter to a custom module or
252 | on of the modules in nodeunit/lib/reporters, when omitted, the default test runner
253 | is used.
254 | * __--list-reporters__ - list available built-in reporters.
255 | * __--config FILE__ - load config options from a JSON file, allows
256 | the customisation of color schemes for the default test reporter etc. See
257 | bin/nodeunit.json for current available options.
258 | * __-t testName__ - run specific test only.
259 | * __-f fullTestName__ - run specific test only. fullTestName is built so: "outerGroup - .. - innerGroup - testName".
260 | * __--version__ or __-v__ - report nodeunit version
261 | * __--help__ - show nodeunit help
262 |
263 |
264 | Running tests in the browser
265 | ----------------------------
266 |
267 | Nodeunit tests can also be run inside the browser. For example usage, see
268 | the examples/browser folder. The basic syntax is as follows:
269 |
270 | __test.html__
271 |
272 |
273 |
274 | Example Test Suite
275 |
276 |
277 |
278 |
279 |
280 |
281 |
Example Test Suite
282 |
288 |
289 |
290 |
291 | Here, `suite1` and `suite2` are just object literals containing test functions
292 | or groups, as would be returned if you did `require('test-suite')` in node.js:
293 |
294 | __suite1.js__
295 |
296 | this.suite1 = {
297 | 'example test': function (test) {
298 | test.ok(true, 'everything is ok');
299 | test.done();
300 | }
301 | };
302 |
303 | If you wish to use a commonjs format for your test suites (using exports), it is
304 | up to you to define the commonjs tools for the browser. There are a number of
305 | alternatives and it's important it fits with your existing code, which is
306 | why nodeunit does not currently provide this out of the box.
307 |
308 | In the example above, the tests will run when the page is loaded.
309 |
310 | The browser-version of nodeunit.js is created in dist/browser when you do, `make
311 | browser`. You'll need [UglifyJS](https://github.com/mishoo/UglifyJS) installed in
312 | order for it to automatically create nodeunit.min.js.
313 |
314 |
315 | Adding nodeunit to Your Projects
316 | --------------------------------
317 |
318 | If you don't want people to have to install the nodeunit command-line tool,
319 | you'll want to create a script that runs the tests for your project with the
320 | correct require paths set up. Here's an example test script, that assumes you
321 | have nodeunit in a suitably located node_modules directory.
322 |
323 | #!/usr/bin/env node
324 | var reporter = require('nodeunit').reporters.default;
325 | reporter.run(['test']);
326 |
327 | If you're using git, you might find it useful to include nodeunit as a
328 | submodule. Using submodules makes it easy for developers to download nodeunit
329 | and run your test suite, without cluttering up your repository with
330 | the source code. To add nodeunit as a git submodule do the following:
331 |
332 | git submodule add git://github.com/caolan/nodeunit.git node_modules/nodeunit
333 |
334 | This will add nodeunit to the node_modules folder of your project. Now, when
335 | cloning the repository, nodeunit can be downloaded by doing the following:
336 |
337 | git submodule init
338 | git submodule update
339 |
340 | Let's update the test script above with a helpful hint on how to get nodeunit,
341 | if it's missing:
342 |
343 | #!/usr/bin/env node
344 | try {
345 | var reporter = require('nodeunit').reporters.default;
346 | }
347 | catch(e) {
348 | console.log("Cannot find nodeunit module.");
349 | console.log("You can download submodules for this project by doing:");
350 | console.log("");
351 | console.log(" git submodule init");
352 | console.log(" git submodule update");
353 | console.log("");
354 | process.exit();
355 | }
356 |
357 | process.chdir(__dirname);
358 | reporter.run(['test']);
359 |
360 | Now if someone attempts to run your test suite without nodeunit installed they
361 | will be prompted to download the submodules for your project.
362 |
363 |
364 | Built-in Test Reporters
365 | -----------------------
366 |
367 | * __default__ - The standard reporter seen in the nodeunit screenshots
368 | * __minimal__ - Pretty, minimal output, shows errors and progress only
369 | * __html__ - Outputs a HTML report to stdout
370 | * __junit__ - Creates jUnit compatible XML reports, which can be used with
371 | continuous integration tools such as [Hudson](http://hudson-ci.org/).
372 | * __machineout__ - Simple reporter for machine analysis. There is
373 | [nodeunit.vim](https://github.com/lambdalisue/nodeunit.vim) which is useful for TDD on VIM.
374 |
375 |
376 | Writing a Test Reporter
377 | ---------------------
378 |
379 | Nodeunit exports runTest(fn, options), runModule(mod, options) and
380 | runFiles(paths, options). You'll most likely want to run test suites from
381 | files, which can be done using the latter function. The _options_ argument can
382 | contain callbacks which run during testing. Nodeunit provides the following
383 | callbacks:
384 |
385 | * __moduleStart(name)__ - called before a module is tested
386 | * __moduleDone(name, assertions)__ - called once all test functions within the
387 | module have completed (see assertions object reference below)
388 | ALL tests within the module
389 | * __testStart(name)__ - called before a test function is run
390 | * __testReady(test)__ - called before a test function is run with the test object that will be passed to the test function
391 | * __testDone(name, assertions)__ - called once a test function has completed
392 | (by calling test.done())
393 | * __log(assertion)__ - called whenever an assertion is made (see assertion
394 | object reference below)
395 | * __done(assertions)__ - called after all tests/modules are complete
396 |
397 | The __assertion__ object:
398 |
399 | * __passed()__ - did the assertion pass?
400 | * __failed()__ - did the assertion fail?
401 | * __error__ - the AssertionError if the assertion failed
402 | * __method__ - the nodeunit assertion method used (ok, same, equals...)
403 | * __message__ - the message the assertion method was called with (optional)
404 |
405 | The __assertionList__ object:
406 |
407 | * An array-like object with the following new attributes:
408 | * __failures()__ - the number of assertions which failed
409 | * __duration__ - the time taken for the test to complete in msecs
410 |
411 | For a reference implementation of a test reporter, see lib/reporters/default.js in
412 | the nodeunit project directory.
413 |
414 |
415 | Sandbox utility
416 | ---------------
417 |
418 | This is a function which evaluates JavaScript files in a sandbox and returns the
419 | context. The sandbox function can be used for testing client-side code or private
420 | un-exported functions within a module.
421 |
422 | var sandbox = require('nodeunit').utils.sandbox;
423 | var example = sandbox('example.js');
424 |
425 | __sandbox(files, sandbox)__ - Evaluates JavaScript files in a sandbox, returning
426 | the context. The first argument can either be a single filename or an array of
427 | filenames. If multiple filenames are given their contents are concatenated before
428 | evaluation. The second argument is an optional context to use for the sandbox.
429 |
430 | Note: When working with the sandbox if your script depends on outside sources
431 | (i.e. using `require`) then you will want to pass that into the optional
432 | context when setting up the sandbox.
433 |
434 | var sandbox = require('nodeunit').utils.sandbox;
435 | // pass in some node globals
436 | var box_globals = {
437 | // Passing module.exports into the sandbox will give your code access to it.
438 | module: {exports: exports},
439 | // Passing require into the sandbox will give your code access to use it AND
440 | // will share the cache with modules already required from outside the sandbox.
441 | require: require,
442 | // Passing console into the sandbox will give your code access to it
443 | console: console
444 | };
445 | var example = sandbox('example.js', box_globals);
446 |
447 |
448 | Running the nodeunit Tests
449 | --------------------------
450 |
451 | The tests for nodeunit are written using nodeunit itself as the test framework.
452 | However, the module test-base.js first does some basic tests using the assert
453 | module to ensure that test functions are actually run, and a basic level of
454 | nodeunit functionality is available.
455 |
456 | To run the nodeunit tests do:
457 |
458 | make test
459 |
460 | __Note:__ There was a bug in node v0.2.0 causing the tests to hang, upgrading
461 | to v0.2.1 fixes this.
462 |
463 |
464 | __machineout__ reporter
465 | ----------------------------------------------
466 |
467 | The default reporter is readable for human but not for machine analysis.
468 | When you want to analyze the output of nodeunit, use __machineout__ reporter and you will get
469 |
470 |
471 |
472 |
473 | nodeunit with vim
474 | ----------------------------------
475 | There is [nodeunit.vim](https://github.com/lambdalisue/nodeunit.vim) so you can use
476 | nodeunit with VIM.
477 |
478 | That compiler uses __machineout__ reporter and it is useful to use
479 | with [vim-makegreen](https://github.com/reinh/vim-makegreen).
480 |
481 |
482 |
483 | Contributing
484 | ------------
485 |
486 | Contributions to the project are most welcome, so feel free to fork and improve.
487 | When submitting a pull request, please run `make lint` first to ensure
488 | we're following a consistent coding style.
489 |
--------------------------------------------------------------------------------