├── test ├── mock │ ├── foo.js │ ├── zop │ │ └── index.js │ └── bar │ │ ├── myModuleLib │ │ ├── extras.js │ │ └── bar.js │ │ └── package.json ├── node_modules │ ├── foobar.js │ └── baz │ │ └── index.js ├── README.md ├── run.js └── lib │ ├── jsmock.js │ └── nodeunit.js ├── README.md └── src └── require.js /test/mock/foo.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/mock/zop/index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/node_modules/foobar.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/node_modules/baz/index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/mock/bar/myModuleLib/extras.js: -------------------------------------------------------------------------------- 1 | exports.name = 'extras'; -------------------------------------------------------------------------------- /test/mock/bar/myModuleLib/bar.js: -------------------------------------------------------------------------------- 1 | exports.name = 'bar'; 2 | exports.extras = require('./extras'); -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | To run the tests, change your current working directory to this test folder. 2 | 3 | Then: 4 | 5 | java -classpath js.jar org.mozilla.javascript.tools.shell.Main run.js 6 | -------------------------------------------------------------------------------- /test/mock/bar/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-test-package", 3 | "version": "1.2.3", 4 | "description": "Blah blah blah", 5 | "keywords": [ 6 | "package", 7 | "example" 8 | ], 9 | "main": "./myModuleLib/bar.js" 10 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Rhino-Require 2 | ==== 3 | 4 | > NOTICE: Although, as of version 1.7R3, Mozilla Rhino has built-in support 5 | > for modules, it lacks support for loading packages (as of this commit date). 6 | > Therefore this project remains available as an alternative and reference 7 | > implementation. 8 | 9 | 10 | A nodejs-like implementation of the commonjs `require` function, implemented 11 | to be compatible with the Mozilla Rhino JavaScript engine. 12 | 13 | Usage 14 | ---- 15 | 16 | Assuming you have created a JavaScript commonjs module and saved it at 17 | `./node_modules/twiddler/index.js` 18 | 19 | module.exports = { 20 | twiddle: function(str) { 21 | } 22 | }; 23 | 24 | You can then load that module into your `./main.js` script using Rhino-Require. 25 | 26 | load('lib/rhino-require.js'); 27 | 28 | var twiddler = require('twiddler'); 29 | twiddler.twiddle('foo'); 30 | 31 | License 32 | ---- 33 | 34 | Written by Michael Mathews. Licensed as public domain. 35 | 36 | -------------------------------------------------------------------------------- /test/run.js: -------------------------------------------------------------------------------- 1 | // USAGE: java -classpath ~/Scripts/js.jar org.mozilla.javascript.tools.shell.Main test.js 2 | 3 | load('./lib/nodeunit.js'); 4 | //load('lib/jsmock.js'); 5 | 6 | load('../src/require.js'); 7 | 8 | module = typeof module === 'undefined'? {} : module; 9 | var test = module.exports = { 10 | 'Basic tests.': { 11 | 'The require function should be defined.': function(t) { 12 | t.expect(1); 13 | t.equal( typeof require, 'function' ); 14 | t.done(); 15 | }, 16 | 'The require function should take one required string argument.': function(t) { 17 | t.expect(3); 18 | t['throws']( function() { require(); }); 19 | t['throws']( function() { require(1); } ); 20 | t['throws']( function() { require(''); } ); 21 | t.done(); 22 | } 23 | }, 24 | 'The require.resolve function.': { 25 | 'The require.resolve function should be defined.': function(t) { 26 | t.expect(1); 27 | t.equal( typeof require.resolve, 'function' ); 28 | t.done(); 29 | }, 30 | 'When an id starts with "./" it should resolve relative to the current working directory.': function(t) { 31 | t.expect(1); 32 | t.equal( require.resolve( './mock/foo'), toAbsolute('./mock/foo.js') ); 33 | t.done(); 34 | }, 35 | 'When an id starts with "./" it should resolve relative to the current running module.': function(t) { 36 | t.expect(1); 37 | require._root.unshift('./mock/bar.js'); 38 | t.equal( require.resolve('./foo'), toAbsolute('./mock/foo.js') ); 39 | require._root.shift(); 40 | t.done(); 41 | }, 42 | 'When an id does not start with "./" it should resolve relative to the cwd.': function(t) { 43 | t.expect(1); 44 | require._root.unshift('blah/one/two.js'); 45 | t.equal( require.resolve('mock/foo'), toAbsolute('./mock/foo.js') ); 46 | require._root.shift(); 47 | t.done(); 48 | } 49 | }, 50 | 'Resolve from package.json.': { 51 | 'The require.resolve function should use the "main" property from package.json.': function(t) { 52 | t.expect(1); 53 | t.equal( require.resolve('./mock/bar'), toAbsolute('./mock/bar/myModuleLib/bar.js') ); 54 | t.done(); 55 | } 56 | }, 57 | 'Resolve from index file.': { 58 | 'The require.resolve function should use the "index.js" file.': function(t) { 59 | t.expect(1); 60 | t.equal( require.resolve('./mock/zop'), toAbsolute('./mock/zop/index.js') ); 61 | t.done(); 62 | } 63 | }, 64 | 'Resolve from require.paths.': { 65 | 'The require.resolve function should use the require.paths values.': function(t) { 66 | t.expect(1); 67 | require.paths.push('./mock'); 68 | t.equal( require.resolve('foo'), toAbsolute('./mock/foo.js') ); 69 | require.paths.pop(); 70 | 71 | t.done(); 72 | } 73 | }, 74 | 'Resolve from node_modules.': { 75 | 'The require.resolve function should use the node_modules dir.': function(t) { 76 | t.expect(1); 77 | t.equal( require.resolve('foobar'), toAbsolute('./node_modules/foobar.js') ); 78 | t.done(); 79 | }, 80 | 'The require.resolve function should look for index in node_modules dir.': function(t) { 81 | t.expect(1); 82 | t.equal( require.resolve('baz'), toAbsolute('./node_modules/baz/index.js') ); 83 | t.done(); 84 | } 85 | }, 86 | 'Require from package.json.': { 87 | 'The required module should be returned when it is listed in package.json.': function(t) { 88 | t.expect(3); 89 | var bar = require('mock/bar'); 90 | t.equal( typeof bar, 'object' ); 91 | t.equal( bar.name, 'bar' ); 92 | t.equal( bar.extras.name, 'extras' ); 93 | t.done(); 94 | }, 95 | 'The required dot-relative module should be returned when it is listed in package.json.': function(t) { 96 | t.expect(2); 97 | var bar = require('./mock/bar'); 98 | t.equal( typeof bar, 'object' ); 99 | t.equal( bar.name, 'bar' ); 100 | t.done(); 101 | } 102 | } 103 | }; 104 | 105 | var SLASH = Packages.java.io.File.separator; 106 | function toAbsolute(relPath) { 107 | var absPath = String( new java.io.File(relPath).getAbsolutePath() ) 108 | .replace(/(\/|\\)[^\/\\]+\/\.\.(\/|\\)/g, SLASH) 109 | .replace(/(\/|\\)\.(\/|\\|$)/g, SLASH); 110 | return absPath; 111 | } 112 | 113 | nodeunit.run(test); -------------------------------------------------------------------------------- /src/require.js: -------------------------------------------------------------------------------- 1 | /* 2 | Rhino-Require is Public Domain 3 | 4 | 5 | The author or authors of this code dedicate any and all copyright interest 6 | in this code to the public domain. We make this dedication for the benefit 7 | of the public at large and to the detriment of our heirs and successors. We 8 | intend this dedication to be an overt act of relinquishment in perpetuity of 9 | all present and future rights to this code under copyright law. 10 | */ 11 | 12 | (function(global) { 13 | 14 | var require = global.require = function(id) { /*debug*///console.log('require('+id+')'); 15 | if (typeof arguments[0] !== 'string') throw 'USAGE: require(moduleId)'; 16 | 17 | var moduleContent = '', 18 | moduleUri; 19 | 20 | moduleUri = require.resolve(id); 21 | moduleContent = ''; 22 | 23 | if (require.cache[moduleUri]) { 24 | return require.cache[moduleUri]; 25 | } 26 | 27 | var file = new java.io.File(moduleUri); 28 | try { 29 | var scanner = new java.util.Scanner(file).useDelimiter("\\Z"); 30 | moduleContent = String( scanner.next() ); 31 | } 32 | catch(e) { 33 | throw 'Unable to read file at: '+moduleUri+', '+e; 34 | } 35 | 36 | if (moduleContent) { 37 | try { 38 | var f = new Function('require', 'exports', 'module', moduleContent), 39 | exports = require.cache[moduleUri] || {}, 40 | module = { id: id, uri: moduleUri, exports: exports }; 41 | 42 | 43 | require._root.unshift(moduleUri); 44 | f.call({}, require, exports, module); 45 | require._root.shift(); 46 | } 47 | catch(e) { 48 | throw 'Unable to require source code from "' + moduleUri + '": ' + e.toSource(); 49 | } 50 | 51 | exports = module.exports || exports; 52 | require.cache[moduleUri] = exports; 53 | } 54 | else { 55 | throw 'The requested module cannot be returned: no content for id: "' + id + '" in paths: ' + require.paths.join(', '); 56 | } 57 | 58 | return exports; 59 | } 60 | require._root = ['']; 61 | require.paths = []; 62 | require.cache = {}; // cache module exports. Like: {id: exported} 63 | 64 | var SLASH = Packages.java.io.File.separator; 65 | 66 | /** Given a module id, try to find the path to the associated module. 67 | */ 68 | require.resolve = function(id) { 69 | // TODO: 1. load node core modules 70 | 71 | // 2. dot-relative module id, like './foo/bar' 72 | var parts = id.match(/^(\.?\.(?:\\|\/)|(?:\\|\/))(.+)$/), 73 | isRelative = false, 74 | isAbsolute = false, 75 | basename = id; 76 | 77 | if (parts) { 78 | isRelative = parts[1] === './' || parts[1] === '.\\' || parts[1] === '../' || parts[1] === '..\\'; 79 | isAbsolute = parts[1] === '/' || parts[1] === '\\'; 80 | basename = parts[2]; 81 | } 82 | 83 | if (typeof basename !== 'undefined') { 84 | 85 | if (isAbsolute) { 86 | rootedId = id; 87 | } 88 | else { 89 | var root = (isRelative? toDir(require._root[0] || '.') : '.'), 90 | rootedId = deDotPath(root + SLASH + id), 91 | uri = ''; 92 | } 93 | 94 | if ( uri = loadAsFile(rootedId) ) { } 95 | else if ( uri = loadAsDir(rootedId) ) { } 96 | else if ( uri = loadNodeModules(rootedId) ) { } 97 | else if ( uri = nodeModulesPaths(rootedId, 'rhino_modules') ) { } 98 | else if ( uri = nodeModulesPaths(rootedId, 'node_modules') ) { } 99 | 100 | if (uri !== '') return toAbsolute(uri); 101 | 102 | throw 'Require Error: Not found.'; 103 | } 104 | } 105 | 106 | /** Given a path, return the base directory of that path. 107 | @example toDir('/foo/bar/somefile.js'); => '/foo/bar' 108 | */ 109 | function toDir(path) { 110 | var file = new java.io.File(path); 111 | 112 | if (file.isDirectory()) { 113 | return path; 114 | } 115 | 116 | var parts = path.split(/[\\\/]/); 117 | parts.pop(); 118 | return parts.join(SLASH); 119 | } 120 | 121 | /** Returns true if the given path exists and is a file. 122 | */ 123 | function isFile(path) { 124 | var file = new java.io.File(path); 125 | 126 | if (file.isFile()) { 127 | return true; 128 | } 129 | 130 | return false; 131 | } 132 | 133 | /** Returns true if the given path exists and is a directory. 134 | */ 135 | function isDir(path) { 136 | var file = new java.io.File(path); 137 | 138 | if (file.isDirectory()) { 139 | return true; 140 | } 141 | 142 | return false; 143 | } 144 | 145 | /** Get the path of the current working directory 146 | */ 147 | function getCwd() { 148 | return deDotPath( toDir( ''+new java.io.File('.').getAbsolutePath() ) ); 149 | } 150 | 151 | function toAbsolute(relPath) { 152 | absPath = ''+new java.io.File(relPath).getAbsolutePath(); 153 | absPath = deDotPath(absPath); 154 | return absPath; 155 | } 156 | 157 | function deDotPath(path) { 158 | return String(path) 159 | .replace(/(\/|\\)[^\/\\]+\/\.\.(\/|\\)/g, SLASH) 160 | .replace(/(\/|\\)\.(\/|\\|$)/g, SLASH); 161 | } 162 | 163 | /** Assume the id is a file, try to find it. 164 | */ 165 | function loadAsFile(id) { 166 | if ( isFile(id) ) { return id; } 167 | 168 | if ( isFile(id+'.js') ) { return id+'.js'; } 169 | 170 | if ( isFile(id+'.node') ) { throw 'Require Error: .node files not supported'; } 171 | } 172 | 173 | /** Assume the id is a directory, try to find a module file within it. 174 | */ 175 | function loadAsDir(id) { 176 | if (!isDir(id)) { 177 | return; 178 | } 179 | // look for the "main" property of the package.json file 180 | if ( isFile(id+SLASH+'package.json') ) { 181 | var packageJson = readFileSync(id+SLASH+'package.json', 'utf-8'); 182 | eval( 'packageJson = '+ packageJson); 183 | if (packageJson.hasOwnProperty('main')) { 184 | var main = deDotPath(id + SLASH + packageJson.main); 185 | return require.resolve(main); 186 | } 187 | } 188 | 189 | if ( isFile(id+SLASH+'index.js') ) { 190 | return id+SLASH+'index.js'; 191 | } 192 | } 193 | 194 | function loadNodeModules(id) { 195 | var path, 196 | uri, 197 | cwd = getCwd(), 198 | dirs = cwd.split(SLASH), 199 | dir = dirs.join(SLASH); 200 | for (var i = 0, len = require.paths.length; i < len; i++) { 201 | path = require.paths[i]; 202 | path = deDotPath(dir + SLASH + path); 203 | if (isDir(path)) { 204 | path = deDotPath(path + SLASH + id); 205 | 206 | uri = loadAsFile(path); 207 | if (typeof uri !== 'undefined') { 208 | return uri; 209 | } 210 | 211 | uri = loadAsDir(path); 212 | if (typeof uri !== 'undefined') { 213 | return uri; 214 | } 215 | } 216 | } 217 | } 218 | 219 | function nodeModulesPaths(id, moduleFolder) { 220 | var cwd = getCwd(), 221 | dirs = cwd.split(SLASH), 222 | dir, 223 | path, 224 | filename, 225 | uri; 226 | 227 | while (dirs.length) { 228 | dir = dirs.join(SLASH); 229 | path = dir+SLASH+moduleFolder; 230 | 231 | if ( isDir(path) ) { 232 | filename = deDotPath(path+SLASH+id); 233 | 234 | if ( uri = loadAsFile(filename) ) { 235 | uri = uri.replace(cwd, '.'); 236 | return uri; 237 | } 238 | 239 | if ( uri = loadAsDir(filename) ) { 240 | uri = uri.replace(cwd, '.'); 241 | return uri; 242 | } 243 | } 244 | 245 | dirs.pop(); 246 | } 247 | } 248 | 249 | function readFileSync(filename, encoding, callback) { 250 | if (typeof arguments[1] === 'function') { 251 | encoding = null; 252 | callback = arguments[1]; 253 | } 254 | 255 | encoding = encoding || java.lang.System.getProperty('file.encoding'); 256 | 257 | try { 258 | var content = new java.util.Scanner( 259 | new java.io.File(filename), 260 | encoding 261 | ).useDelimiter("\\Z"); 262 | 263 | return String( content.next() ); 264 | } 265 | catch (e) { 266 | return ''; 267 | } 268 | } 269 | 270 | })(this); 271 | -------------------------------------------------------------------------------- /test/lib/jsmock.js: -------------------------------------------------------------------------------- 1 | /* 2 | * JSMock 1.2.2, a mock object library for JavaScript 3 | * Copyright (C) 2006 Justin DeWind 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | JSMock = { 21 | extend: function(object) { 22 | var mockControl = new MockControl(); 23 | object.createMock = function(objectToMock) {return mockControl.createMock(objectToMock)}; 24 | object.resetMocks = function() {mockControl.reset()}; 25 | object.verifyMocks = function() {mockControl.verify()}; 26 | 27 | if(!object.tearDown) { 28 | object.tearDown = function() { 29 | object.verifyMocks(); 30 | } 31 | } 32 | else if(object.tearDown.constructor == Function) { 33 | object.__oldTearDown__ = object.tearDown; 34 | object.tearDown = function() { 35 | object.__oldTearDown__(); 36 | object.verifyMocks(); 37 | } 38 | } 39 | } 40 | } 41 | 42 | function MockControl() { 43 | this.__expectationMatcher = new ExpectationMatcher(); 44 | this.__lastMock = null; 45 | this.__lastCallName = null; 46 | } 47 | 48 | MockControl.prototype = { 49 | 50 | createMock: function(objectToMock) { 51 | var mock = { calls: [], expects: function() {this.__recording = true; return this}, __recording: false}; 52 | mock.expect = mock.expects; 53 | 54 | if(objectToMock != null) { 55 | 56 | if( typeof(objectToMock) == 'function' ) { 57 | this.__createMethods(objectToMock, mock); 58 | this.__createMethods(new objectToMock(), mock); 59 | } 60 | else if( typeof(objectToMock) == 'object') { 61 | this.__createMethods(objectToMock, mock); 62 | } 63 | else { 64 | throw new Error("Cannot mock out a " + typeof(objectToMock)); 65 | } 66 | 67 | } 68 | 69 | var self = this; 70 | mock.addMockMethod = function(method) { self.__createMethod(self, mock, method); } 71 | 72 | return mock; 73 | }, 74 | 75 | andReturn: function(returnValue) { 76 | this.__verifyLastMockNotNull("Cannot set return value without an expectation"); 77 | this.__initializeReturnExpectationForMock(); 78 | this.__lastMock.calls[this.__lastCallName].push( function() { return returnValue; }); 79 | }, 80 | 81 | andThrow: function(throwMsg) { 82 | this.__verifyLastMockNotNull("Cannot throw error without an expectation"); 83 | this.__initializeReturnExpectationForMock(); 84 | this.__lastMock.calls[this.__lastCallName].push( function() { throw new Error(throwMsg); }); 85 | }, 86 | 87 | andStub: function(block) { 88 | this.__verifyLastMockNotNull("Cannot stub without an expectation"); 89 | if( typeof(block) != 'function') { 90 | throw new Error("Stub must be a function"); 91 | } 92 | this.__initializeReturnExpectationForMock(); 93 | this.__lastMock.calls[this.__lastCallName].push( function() { return block.apply(this, arguments); }); 94 | }, 95 | 96 | reset: function() { 97 | this.__expectationMatcher.reset(); 98 | }, 99 | 100 | verify: function() { 101 | if(!this.__expectationMatcher.matches()) 102 | { 103 | discrepancy = this.__expectationMatcher.discrepancy(); 104 | message = discrepancy.message; 105 | method = discrepancy.behavior.method 106 | formattedArgs = ArgumentFormatter.format(discrepancy.behavior.methodArguments); 107 | this.__expectationMatcher.reset(); 108 | throw new Error(message + ": " + method + "(" + formattedArgs + ")"); 109 | } 110 | else { 111 | this.__expectationMatcher.reset(); 112 | } 113 | 114 | }, 115 | 116 | __createMethods: function(object, mock) { 117 | for( property in object ) { 118 | if( this.__isPublicMethod(object, property) ) { 119 | this.__createMethod( this, mock, property ); 120 | } 121 | } 122 | }, 123 | 124 | __createMethod: function(control, mock, method) { 125 | mock[method] = 126 | function() { 127 | if( mock.__recording ) { 128 | control.__lastMock = mock; 129 | control.__lastCallName = method; 130 | control.__expectationMatcher.addExpectedMethodCall( mock, method, arguments ); 131 | mock.__recording = false; 132 | return control; 133 | } 134 | else { 135 | control.__expectationMatcher.addActualMethodCall( mock, method, arguments ); 136 | if( mock.calls[method] != null) { 137 | returnValue = mock.calls[method].shift(); 138 | if( typeof(returnValue) == 'function') { 139 | return returnValue.apply(this, arguments); 140 | } 141 | } 142 | } 143 | } 144 | }, 145 | 146 | __isPublicMethod: function(object, property) { 147 | try { 148 | return typeof(object[property]) == 'function' && property.charAt(0) != "_"; 149 | } catch(e) { 150 | return false; 151 | } 152 | }, 153 | 154 | __verifyLastMockNotNull: function(throwMsg) { 155 | if(this.__lastMock == null) { 156 | throw new Error(throwMsg); 157 | } 158 | }, 159 | 160 | __initializeReturnExpectationForMock: function() { 161 | if(typeof(this.__lastMock.calls[this.__lastCallName]) == 'undefined') { 162 | this.__lastMock.calls[this.__lastCallName] = []; 163 | } 164 | } 165 | } 166 | 167 | function ExpectationMatcher() { 168 | this.__expectationBehaviorList = []; 169 | this.__actualBehaviorList = []; 170 | this.__discrepancy = null; 171 | 172 | } 173 | 174 | ExpectationMatcher.prototype = { 175 | addExpectedMethodCall: function(caller, method, methodArguments ) { 176 | this.__expectationBehaviorList.push(new InvocationBehavior(caller, method, methodArguments)); 177 | }, 178 | 179 | addActualMethodCall: function(caller, method, methodArguments ) { 180 | this.__actualBehaviorList.push(new InvocationBehavior(caller, method, methodArguments)); 181 | }, 182 | 183 | matches: function() { 184 | var self = this; 185 | var matches = true; 186 | 187 | this.__expectationBehaviorList.eachIndexForJsMock(function(index, expectedBehavior) { 188 | var actualBehavior = (self.__actualBehaviorList.length > index) ? self.__actualBehaviorList[index] : null; 189 | 190 | if(matches) { 191 | if( actualBehavior === null ) { 192 | self.__discrepancy = new Discrepancy("Expected function not called", expectedBehavior); 193 | matches = false; 194 | } 195 | else if( expectedBehavior.method != actualBehavior.method ) { 196 | self.__discrepancy = new Discrepancy("Surprise call", actualBehavior); 197 | matches = false; 198 | } 199 | else if( expectedBehavior.caller != actualBehavior.caller ) { 200 | self.__discrepancy = new Discrepancy("Surprise call from unexpected caller", actualBehavior); 201 | matches = false; 202 | } 203 | else if( !self.__matchArguments(expectedBehavior.methodArguments, actualBehavior.methodArguments) ) { 204 | self.__discrepancy = new Discrepancy("Unexpected Arguments", actualBehavior); 205 | matches = false; 206 | } 207 | } 208 | }); 209 | 210 | if( this.__actualBehaviorList.length > this.__expectationBehaviorList.length && matches ) { 211 | this.__discrepancy = new Discrepancy("Surprise call", this.__actualBehaviorList[this.__expectationBehaviorList.length]); 212 | matches = false 213 | } 214 | 215 | return matches; 216 | }, 217 | 218 | reset: function() { 219 | this.__expectationBehaviorList = []; 220 | this.__actualBehaviorList = []; 221 | this.__discrepancy = null; 222 | }, 223 | 224 | discrepancy: function() { 225 | return this.__discrepancy; 226 | }, 227 | 228 | __matchArguments: function(expectedArgs, actualArgs) { 229 | var expectedArray = this.__convertArgumentsToArray(expectedArgs); 230 | var actualArray = this.__convertArgumentsToArray(actualArgs); 231 | return ArgumentMatcher.matches(expectedArray, actualArray); 232 | }, 233 | 234 | __convertArgumentsToArray: function(args) { 235 | var convertedArguments = []; 236 | 237 | for(var i = 0; i < args.length; i++) { 238 | convertedArguments[i] = args[i]; 239 | } 240 | 241 | return convertedArguments; 242 | } 243 | } 244 | 245 | function InvocationBehavior(caller, method, methodArguments) { 246 | this.caller = caller; 247 | this.method = method; 248 | this.methodArguments = methodArguments; 249 | } 250 | 251 | function TypeOf(type) { 252 | if(typeof(type) != 'function') 253 | throw new Error("Can only take constructors"); 254 | 255 | this.type = type; 256 | } 257 | 258 | TypeOf.isA = function(type) { return new TypeOf(type); }; 259 | 260 | ArgumentMatcher = { 261 | 262 | matches: function(expected, actual) { 263 | return this.__delegateMatching(expected, actual); 264 | }, 265 | 266 | __delegateMatching: function(expected, actual) { 267 | if( expected == null ) { 268 | return this.__match( expected, actual ); 269 | } 270 | else if( expected.constructor == TypeOf ) { 271 | return this.__match(expected.type, actual.constructor); 272 | } 273 | else if( expected.constructor == Array ) { 274 | return this.__matchArrays(expected, actual); 275 | } 276 | else { 277 | return this.__match(expected, actual); 278 | } 279 | }, 280 | 281 | __match: function(expected, actual) { 282 | return ( expected == actual ); 283 | }, 284 | 285 | __matchArrays: function(expected, actual) { 286 | if ( actual == null) 287 | return false; 288 | 289 | if( actual.constructor != Array) 290 | return false; 291 | 292 | if( expected.length != actual.length ) 293 | return false; 294 | 295 | for(var i = 0; i < expected.length; i++ ) { 296 | if( !this.__delegateMatching(expected[i], actual[i]) ) 297 | return false; 298 | } 299 | 300 | return true; 301 | } 302 | } 303 | 304 | function Discrepancy(message, behavior) { 305 | if(behavior.constructor != InvocationBehavior) 306 | throw new Error("The behavior can only be an InvocationBehavior object"); 307 | 308 | this.message = message; 309 | this.behavior = behavior; 310 | } 311 | 312 | ArgumentFormatter = { 313 | 314 | format: function(args) { 315 | var formattedArgs = ""; 316 | for(var i = 0; i < args.length; i++) { 317 | if( args[i] == null ) { 318 | formattedArgs += ( formattedArgs == "" ) ? "null" : ", " + "null"; 319 | } 320 | else if( args[i].constructor == TypeOf || args[i].constructor == Function) { 321 | var func = ( args[i].constructor == TypeOf ) ? args[i].type : args[i]; 322 | formattedArgs += ( formattedArgs == "" ) ? this.__formatFunction(func) : ", " + this.__formatFunction(func); 323 | } 324 | else if( typeof(args[i]) == "string" ) { 325 | formattedArgs += ( formattedArgs == "" ) ? "\"" + args[i].toString() + "\"" : ", \"" + args[i].toString() + "\"" 326 | } 327 | else if( args[i].constructor == Array ) { 328 | formattedArgs += ( formattedArgs == "" ) ? "[" + this.format(args[i]) + "]" : ", [" + this.format(args[i]) + "]"; 329 | } 330 | else { 331 | formattedArgs += ( formattedArgs == "" ) ? args[i].toString() : ", " + args[i].toString(); 332 | } 333 | } 334 | return formattedArgs; 335 | }, 336 | 337 | __formatFunction: function(func) { 338 | // Manual checking is done for internal/native functions 339 | // since Safari will not display them correctly 340 | // for the intended regex parsing. 341 | 342 | if(func == Array) { 343 | return "Array"; 344 | } else if(func == Date) { 345 | return "Date"; 346 | } else if(func == Object) { 347 | return "Object"; 348 | } else if(func == String) { 349 | return "String"; 350 | } else if(func == Function) { 351 | return "Function"; 352 | } else if(func == RegExp) { 353 | return "RegExp"; 354 | } else if(func == Error) { 355 | return "Error"; 356 | } else if(func == Number) { 357 | return "Number"; 358 | } else if(func == Boolean) { 359 | return "Boolean"; 360 | } 361 | var formattedFunc = func.toString().match(/function (\w+)/); 362 | 363 | return ( formattedFunc == null ) ? "{{Closure}}" : formattedFunc[1]; 364 | } 365 | 366 | } 367 | 368 | /* Helpers */ 369 | 370 | // Implemented each method with a unique name to avoid conflicting 371 | // with other libraries that implement it. 372 | Array.prototype.eachIndexForJsMock = function(block) { 373 | for(var index = 0; index < this.length; index++) 374 | { 375 | block(index, this[index]); 376 | } 377 | } 378 | -------------------------------------------------------------------------------- /test/lib/nodeunit.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Nodeunit 3 | * https://github.com/caolan/nodeunit 4 | * Copyright (c) 2010 Caolan McMahon 5 | * MIT Licensed 6 | * 7 | * json2.js 8 | * http://www.JSON.org/json2.js 9 | * Public Domain. 10 | * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 11 | */ 12 | nodeunit = (function(){ 13 | /* 14 | http://www.JSON.org/json2.js 15 | 2010-11-17 16 | 17 | Public Domain. 18 | 19 | NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 20 | 21 | See http://www.JSON.org/js.html 22 | 23 | 24 | This code should be minified before deployment. 25 | See http://javascript.crockford.com/jsmin.html 26 | 27 | USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO 28 | NOT CONTROL. 29 | 30 | 31 | This file creates a global JSON object containing two methods: stringify 32 | and parse. 33 | 34 | JSON.stringify(value, replacer, space) 35 | value any JavaScript value, usually an object or array. 36 | 37 | replacer an optional parameter that determines how object 38 | values are stringified for objects. It can be a 39 | function or an array of strings. 40 | 41 | space an optional parameter that specifies the indentation 42 | of nested structures. If it is omitted, the text will 43 | be packed without extra whitespace. If it is a number, 44 | it will specify the number of spaces to indent at each 45 | level. If it is a string (such as '\t' or ' '), 46 | it contains the characters used to indent at each level. 47 | 48 | This method produces a JSON text from a JavaScript value. 49 | 50 | When an object value is found, if the object contains a toJSON 51 | method, its toJSON method will be called and the result will be 52 | stringified. A toJSON method does not serialize: it returns the 53 | value represented by the name/value pair that should be serialized, 54 | or undefined if nothing should be serialized. The toJSON method 55 | will be passed the key associated with the value, and this will be 56 | bound to the value 57 | 58 | For example, this would serialize Dates as ISO strings. 59 | 60 | Date.prototype.toJSON = function (key) { 61 | function f(n) { 62 | // Format integers to have at least two digits. 63 | return n < 10 ? '0' + n : n; 64 | } 65 | 66 | return this.getUTCFullYear() + '-' + 67 | f(this.getUTCMonth() + 1) + '-' + 68 | f(this.getUTCDate()) + 'T' + 69 | f(this.getUTCHours()) + ':' + 70 | f(this.getUTCMinutes()) + ':' + 71 | f(this.getUTCSeconds()) + 'Z'; 72 | }; 73 | 74 | You can provide an optional replacer method. It will be passed the 75 | key and value of each member, with this bound to the containing 76 | object. The value that is returned from your method will be 77 | serialized. If your method returns undefined, then the member will 78 | be excluded from the serialization. 79 | 80 | If the replacer parameter is an array of strings, then it will be 81 | used to select the members to be serialized. It filters the results 82 | such that only members with keys listed in the replacer array are 83 | stringified. 84 | 85 | Values that do not have JSON representations, such as undefined or 86 | functions, will not be serialized. Such values in objects will be 87 | dropped; in arrays they will be replaced with null. You can use 88 | a replacer function to replace those with JSON values. 89 | JSON.stringify(undefined) returns undefined. 90 | 91 | The optional space parameter produces a stringification of the 92 | value that is filled with line breaks and indentation to make it 93 | easier to read. 94 | 95 | If the space parameter is a non-empty string, then that string will 96 | be used for indentation. If the space parameter is a number, then 97 | the indentation will be that many spaces. 98 | 99 | Example: 100 | 101 | text = JSON.stringify(['e', {pluribus: 'unum'}]); 102 | // text is '["e",{"pluribus":"unum"}]' 103 | 104 | 105 | text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); 106 | // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' 107 | 108 | text = JSON.stringify([new Date()], function (key, value) { 109 | return this[key] instanceof Date ? 110 | 'Date(' + this[key] + ')' : value; 111 | }); 112 | // text is '["Date(---current time---)"]' 113 | 114 | 115 | JSON.parse(text, reviver) 116 | This method parses a JSON text to produce an object or array. 117 | It can throw a SyntaxError exception. 118 | 119 | The optional reviver parameter is a function that can filter and 120 | transform the results. It receives each of the keys and values, 121 | and its return value is used instead of the original value. 122 | If it returns what it received, then the structure is not modified. 123 | If it returns undefined then the member is deleted. 124 | 125 | Example: 126 | 127 | // Parse the text. Values that look like ISO date strings will 128 | // be converted to Date objects. 129 | 130 | myData = JSON.parse(text, function (key, value) { 131 | var a; 132 | if (typeof value === 'string') { 133 | a = 134 | /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); 135 | if (a) { 136 | return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], 137 | +a[5], +a[6])); 138 | } 139 | } 140 | return value; 141 | }); 142 | 143 | myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { 144 | var d; 145 | if (typeof value === 'string' && 146 | value.slice(0, 5) === 'Date(' && 147 | value.slice(-1) === ')') { 148 | d = new Date(value.slice(5, -1)); 149 | if (d) { 150 | return d; 151 | } 152 | } 153 | return value; 154 | }); 155 | 156 | 157 | This is a reference implementation. You are free to copy, modify, or 158 | redistribute. 159 | */ 160 | 161 | /*jslint evil: true, strict: false, regexp: false */ 162 | 163 | /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, 164 | call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, 165 | getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, 166 | lastIndex, length, parse, prototype, push, replace, slice, stringify, 167 | test, toJSON, toString, valueOf 168 | */ 169 | 170 | 171 | // Create a JSON object only if one does not already exist. We create the 172 | // methods in a closure to avoid creating global variables. 173 | 174 | if (!this.JSON) { 175 | this.JSON = {}; 176 | } 177 | 178 | (function () { 179 | "use strict"; 180 | 181 | function f(n) { 182 | // Format integers to have at least two digits. 183 | return n < 10 ? '0' + n : n; 184 | } 185 | 186 | if (typeof Date.prototype.toJSON !== 'function') { 187 | 188 | Date.prototype.toJSON = function (key) { 189 | 190 | return isFinite(this.valueOf()) ? 191 | this.getUTCFullYear() + '-' + 192 | f(this.getUTCMonth() + 1) + '-' + 193 | f(this.getUTCDate()) + 'T' + 194 | f(this.getUTCHours()) + ':' + 195 | f(this.getUTCMinutes()) + ':' + 196 | f(this.getUTCSeconds()) + 'Z' : null; 197 | }; 198 | 199 | String.prototype.toJSON = 200 | Number.prototype.toJSON = 201 | Boolean.prototype.toJSON = function (key) { 202 | return this.valueOf(); 203 | }; 204 | } 205 | 206 | var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 207 | escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 208 | gap, 209 | indent, 210 | meta = { // table of character substitutions 211 | '\b': '\\b', 212 | '\t': '\\t', 213 | '\n': '\\n', 214 | '\f': '\\f', 215 | '\r': '\\r', 216 | '"' : '\\"', 217 | '\\': '\\\\' 218 | }, 219 | rep; 220 | 221 | 222 | function quote(string) { 223 | 224 | // If the string contains no control characters, no quote characters, and no 225 | // backslash characters, then we can safely slap some quotes around it. 226 | // Otherwise we must also replace the offending characters with safe escape 227 | // sequences. 228 | 229 | escapable.lastIndex = 0; 230 | return escapable.test(string) ? 231 | '"' + string.replace(escapable, function (a) { 232 | var c = meta[a]; 233 | return typeof c === 'string' ? c : 234 | '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 235 | }) + '"' : 236 | '"' + string + '"'; 237 | } 238 | 239 | 240 | function str(key, holder) { 241 | 242 | // Produce a string from holder[key]. 243 | 244 | var i, // The loop counter. 245 | k, // The member key. 246 | v, // The member value. 247 | length, 248 | mind = gap, 249 | partial, 250 | value = holder[key]; 251 | 252 | // If the value has a toJSON method, call it to obtain a replacement value. 253 | 254 | if (value && typeof value === 'object' && 255 | typeof value.toJSON === 'function') { 256 | value = value.toJSON(key); 257 | } 258 | 259 | // If we were called with a replacer function, then call the replacer to 260 | // obtain a replacement value. 261 | 262 | if (typeof rep === 'function') { 263 | value = rep.call(holder, key, value); 264 | } 265 | 266 | // What happens next depends on the value's type. 267 | 268 | switch (typeof value) { 269 | case 'string': 270 | return quote(value); 271 | 272 | case 'number': 273 | 274 | // JSON numbers must be finite. Encode non-finite numbers as null. 275 | 276 | return isFinite(value) ? String(value) : 'null'; 277 | 278 | case 'boolean': 279 | case 'null': 280 | 281 | // If the value is a boolean or null, convert it to a string. Note: 282 | // typeof null does not produce 'null'. The case is included here in 283 | // the remote chance that this gets fixed someday. 284 | 285 | return String(value); 286 | 287 | // If the type is 'object', we might be dealing with an object or an array or 288 | // null. 289 | 290 | case 'object': 291 | 292 | // Due to a specification blunder in ECMAScript, typeof null is 'object', 293 | // so watch out for that case. 294 | 295 | if (!value) { 296 | return 'null'; 297 | } 298 | 299 | // Make an array to hold the partial results of stringifying this object value. 300 | 301 | gap += indent; 302 | partial = []; 303 | 304 | // Is the value an array? 305 | 306 | if (Object.prototype.toString.apply(value) === '[object Array]') { 307 | 308 | // The value is an array. Stringify every element. Use null as a placeholder 309 | // for non-JSON values. 310 | 311 | length = value.length; 312 | for (i = 0; i < length; i += 1) { 313 | partial[i] = str(i, value) || 'null'; 314 | } 315 | 316 | // Join all of the elements together, separated with commas, and wrap them in 317 | // brackets. 318 | 319 | v = partial.length === 0 ? '[]' : 320 | gap ? '[\n' + gap + 321 | partial.join(',\n' + gap) + '\n' + 322 | mind + ']' : 323 | '[' + partial.join(',') + ']'; 324 | gap = mind; 325 | return v; 326 | } 327 | 328 | // If the replacer is an array, use it to select the members to be stringified. 329 | 330 | if (rep && typeof rep === 'object') { 331 | length = rep.length; 332 | for (i = 0; i < length; i += 1) { 333 | k = rep[i]; 334 | if (typeof k === 'string') { 335 | v = str(k, value); 336 | if (v) { 337 | partial.push(quote(k) + (gap ? ': ' : ':') + v); 338 | } 339 | } 340 | } 341 | } else { 342 | 343 | // Otherwise, iterate through all of the keys in the object. 344 | 345 | for (k in value) { 346 | if (Object.hasOwnProperty.call(value, k)) { 347 | v = str(k, value); 348 | if (v) { 349 | partial.push(quote(k) + (gap ? ': ' : ':') + v); 350 | } 351 | } 352 | } 353 | } 354 | 355 | // Join all of the member texts together, separated with commas, 356 | // and wrap them in braces. 357 | 358 | v = partial.length === 0 ? '{}' : 359 | gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + 360 | mind + '}' : '{' + partial.join(',') + '}'; 361 | gap = mind; 362 | return v; 363 | } 364 | } 365 | 366 | // If the JSON object does not yet have a stringify method, give it one. 367 | 368 | if (typeof JSON.stringify !== 'function') { 369 | JSON.stringify = function (value, replacer, space) { 370 | 371 | // The stringify method takes a value and an optional replacer, and an optional 372 | // space parameter, and returns a JSON text. The replacer can be a function 373 | // that can replace values, or an array of strings that will select the keys. 374 | // A default replacer method can be provided. Use of the space parameter can 375 | // produce text that is more easily readable. 376 | 377 | var i; 378 | gap = ''; 379 | indent = ''; 380 | 381 | // If the space parameter is a number, make an indent string containing that 382 | // many spaces. 383 | 384 | if (typeof space === 'number') { 385 | for (i = 0; i < space; i += 1) { 386 | indent += ' '; 387 | } 388 | 389 | // If the space parameter is a string, it will be used as the indent string. 390 | 391 | } else if (typeof space === 'string') { 392 | indent = space; 393 | } 394 | 395 | // If there is a replacer, it must be a function or an array. 396 | // Otherwise, throw an error. 397 | 398 | rep = replacer; 399 | if (replacer && typeof replacer !== 'function' && 400 | (typeof replacer !== 'object' || 401 | typeof replacer.length !== 'number')) { 402 | throw new Error('JSON.stringify'); 403 | } 404 | 405 | // Make a fake root object containing our value under the key of ''. 406 | // Return the result of stringifying the value. 407 | 408 | return str('', {'': value}); 409 | }; 410 | } 411 | 412 | 413 | // If the JSON object does not yet have a parse method, give it one. 414 | 415 | if (typeof JSON.parse !== 'function') { 416 | JSON.parse = function (text, reviver) { 417 | 418 | // The parse method takes a text and an optional reviver function, and returns 419 | // a JavaScript value if the text is a valid JSON text. 420 | 421 | var j; 422 | 423 | function walk(holder, key) { 424 | 425 | // The walk method is used to recursively walk the resulting structure so 426 | // that modifications can be made. 427 | 428 | var k, v, value = holder[key]; 429 | if (value && typeof value === 'object') { 430 | for (k in value) { 431 | if (Object.hasOwnProperty.call(value, k)) { 432 | v = walk(value, k); 433 | if (v !== undefined) { 434 | value[k] = v; 435 | } else { 436 | delete value[k]; 437 | } 438 | } 439 | } 440 | } 441 | return reviver.call(holder, key, value); 442 | } 443 | 444 | 445 | // Parsing happens in four stages. In the first stage, we replace certain 446 | // Unicode characters with escape sequences. JavaScript handles many characters 447 | // incorrectly, either silently deleting them, or treating them as line endings. 448 | 449 | text = String(text); 450 | cx.lastIndex = 0; 451 | if (cx.test(text)) { 452 | text = text.replace(cx, function (a) { 453 | return '\\u' + 454 | ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 455 | }); 456 | } 457 | 458 | // In the second stage, we run the text against regular expressions that look 459 | // for non-JSON patterns. We are especially concerned with '()' and 'new' 460 | // because they can cause invocation, and '=' because it can cause mutation. 461 | // But just to be safe, we want to reject all unexpected forms. 462 | 463 | // We split the second stage into 4 regexp operations in order to work around 464 | // crippling inefficiencies in IE's and Safari's regexp engines. First we 465 | // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we 466 | // replace all simple value tokens with ']' characters. Third, we delete all 467 | // open brackets that follow a colon or comma or that begin the text. Finally, 468 | // we look to see that the remaining characters are only whitespace or ']' or 469 | // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. 470 | 471 | if (/^[\],:{}\s]*$/ 472 | .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') 473 | .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') 474 | .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { 475 | 476 | // In the third stage we use the eval function to compile the text into a 477 | // JavaScript structure. The '{' operator is subject to a syntactic ambiguity 478 | // in JavaScript: it can begin a block or an object literal. We wrap the text 479 | // in parens to eliminate the ambiguity. 480 | 481 | j = eval('(' + text + ')'); 482 | 483 | // In the optional fourth stage, we recursively walk the new structure, passing 484 | // each name/value pair to a reviver function for possible transformation. 485 | 486 | return typeof reviver === 'function' ? 487 | walk({'': j}, '') : j; 488 | } 489 | 490 | // If the text is not JSON parseable, then a SyntaxError is thrown. 491 | 492 | throw new SyntaxError('JSON.parse'); 493 | }; 494 | } 495 | }()); 496 | var assert = {}; 497 | var types = {}; 498 | var core = {}; 499 | var nodeunit = {}; 500 | var reporter = {}; 501 | (function(){ 502 | 503 | var async = {}; 504 | 505 | // global on the server, window in the browser 506 | var root = this; 507 | var previous_async = root.async; 508 | 509 | if(typeof module !== 'undefined' && module.exports) module.exports = async; 510 | else root.async = async; 511 | 512 | async.noConflict = function(){ 513 | root.async = previous_async; 514 | return async; 515 | }; 516 | 517 | //// cross-browser compatiblity functions //// 518 | 519 | var _forEach = function(arr, iterator){ 520 | if(arr.forEach) return arr.forEach(iterator); 521 | for(var i=0; i b ? 1 : 0; 765 | }), function(x){return x.value;})); 766 | }) 767 | }; 768 | 769 | async.auto = function(tasks, callback){ 770 | callback = callback || function(){}; 771 | var keys = _keys(tasks); 772 | if(!keys.length) return callback(null); 773 | 774 | var completed = []; 775 | 776 | var listeners = []; 777 | var addListener = function(fn){ 778 | listeners.unshift(fn); 779 | }; 780 | var removeListener = function(fn){ 781 | for(var i=0; i 966 | // 967 | // Permission is hereby granted, free of charge, to any person obtaining a copy 968 | // of this software and associated documentation files (the 'Software'), to 969 | // deal in the Software without restriction, including without limitation the 970 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 971 | // sell copies of the Software, and to permit persons to whom the Software is 972 | // furnished to do so, subject to the following conditions: 973 | // 974 | // The above copyright notice and this permission notice shall be included in 975 | // all copies or substantial portions of the Software. 976 | // 977 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 978 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 979 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 980 | // AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 981 | // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 982 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 983 | 984 | 985 | var pSlice = Array.prototype.slice; 986 | 987 | // 1. The assert module provides functions that throw 988 | // AssertionError's when particular conditions are not met. The 989 | // assert module must conform to the following interface. 990 | 991 | var assert = exports; 992 | 993 | // 2. The AssertionError is defined in assert. 994 | // new assert.AssertionError({message: message, actual: actual, expected: expected}) 995 | 996 | assert.AssertionError = function AssertionError (options) { 997 | this.name = "AssertionError"; 998 | this.message = options.message; 999 | this.actual = options.actual; 1000 | this.expected = options.expected; 1001 | this.operator = options.operator; 1002 | var stackStartFunction = options.stackStartFunction || fail; 1003 | 1004 | if (Error.captureStackTrace) { 1005 | Error.captureStackTrace(this, stackStartFunction); 1006 | } 1007 | }; 1008 | // code from util.inherits in node 1009 | assert.AssertionError.super_ = Error; 1010 | 1011 | 1012 | // EDITED FOR BROWSER COMPATIBILITY: replaced Object.create call 1013 | // TODO: test what effect this may have 1014 | var ctor = function () { this.constructor = assert.AssertionError; }; 1015 | ctor.prototype = Error.prototype; 1016 | assert.AssertionError.prototype = new ctor(); 1017 | 1018 | 1019 | assert.AssertionError.prototype.toString = function() { 1020 | if (this.message) { 1021 | return [this.name+":", this.message].join(' '); 1022 | } else { 1023 | return [ this.name+":" 1024 | , JSON.stringify(this.expected ) 1025 | , this.operator 1026 | , JSON.stringify(this.actual) 1027 | ].join(" "); 1028 | } 1029 | }; 1030 | 1031 | // assert.AssertionError instanceof Error 1032 | 1033 | assert.AssertionError.__proto__ = Error.prototype; 1034 | 1035 | // At present only the three keys mentioned above are used and 1036 | // understood by the spec. Implementations or sub modules can pass 1037 | // other keys to the AssertionError's constructor - they will be 1038 | // ignored. 1039 | 1040 | // 3. All of the following functions must throw an AssertionError 1041 | // when a corresponding condition is not met, with a message that 1042 | // may be undefined if not provided. All assertion methods provide 1043 | // both the actual and expected values to the assertion error for 1044 | // display purposes. 1045 | 1046 | function fail(actual, expected, message, operator, stackStartFunction) { 1047 | throw new assert.AssertionError({ 1048 | message: message, 1049 | actual: actual, 1050 | expected: expected, 1051 | operator: operator, 1052 | stackStartFunction: stackStartFunction 1053 | }); 1054 | } 1055 | 1056 | // EXTENSION! allows for well behaved errors defined elsewhere. 1057 | assert.fail = fail; 1058 | 1059 | // 4. Pure assertion tests whether a value is truthy, as determined 1060 | // by !!guard. 1061 | // assert.ok(guard, message_opt); 1062 | // This statement is equivalent to assert.equal(true, guard, 1063 | // message_opt);. To test strictly for the value true, use 1064 | // assert.strictEqual(true, guard, message_opt);. 1065 | 1066 | assert.ok = function ok(value, message) { 1067 | if (!!!value) fail(value, true, message, "==", assert.ok); 1068 | }; 1069 | 1070 | // 5. The equality assertion tests shallow, coercive equality with 1071 | // ==. 1072 | // assert.equal(actual, expected, message_opt); 1073 | 1074 | assert.equal = function equal(actual, expected, message) { 1075 | if (actual != expected) fail(actual, expected, message, "==", assert.equal); 1076 | }; 1077 | 1078 | // 6. The non-equality assertion tests for whether two objects are not equal 1079 | // with != assert.notEqual(actual, expected, message_opt); 1080 | 1081 | assert.notEqual = function notEqual(actual, expected, message) { 1082 | if (actual == expected) { 1083 | fail(actual, expected, message, "!=", assert.notEqual); 1084 | } 1085 | }; 1086 | 1087 | // 7. The equivalence assertion tests a deep equality relation. 1088 | // assert.deepEqual(actual, expected, message_opt); 1089 | 1090 | assert.deepEqual = function deepEqual(actual, expected, message) { 1091 | if (!_deepEqual(actual, expected)) { 1092 | fail(actual, expected, message, "deepEqual", assert.deepEqual); 1093 | } 1094 | }; 1095 | 1096 | function _deepEqual(actual, expected) { 1097 | // 7.1. All identical values are equivalent, as determined by ===. 1098 | if (actual === expected) { 1099 | return true; 1100 | 1101 | } else if (Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) { 1102 | if (actual.length != expected.length) return false; 1103 | 1104 | for (var i = 0; i < actual.length; i++) { 1105 | if (actual[i] !== expected[i]) return false; 1106 | } 1107 | 1108 | return true; 1109 | 1110 | // 7.2. If the expected value is a Date object, the actual value is 1111 | // equivalent if it is also a Date object that refers to the same time. 1112 | } else if (actual instanceof Date && expected instanceof Date) { 1113 | return actual.getTime() === expected.getTime(); 1114 | 1115 | // 7.3. Other pairs that do not both pass typeof value == "object", 1116 | // equivalence is determined by ==. 1117 | } else if (typeof actual != 'object' && typeof expected != 'object') { 1118 | return actual == expected; 1119 | 1120 | // 7.4. For all other Object pairs, including Array objects, equivalence is 1121 | // determined by having the same number of owned properties (as verified 1122 | // with Object.prototype.hasOwnProperty.call), the same set of keys 1123 | // (although not necessarily the same order), equivalent values for every 1124 | // corresponding key, and an identical "prototype" property. Note: this 1125 | // accounts for both named and indexed properties on Arrays. 1126 | } else { 1127 | return objEquiv(actual, expected); 1128 | } 1129 | } 1130 | 1131 | function isUndefinedOrNull (value) { 1132 | return value === null || value === undefined; 1133 | } 1134 | 1135 | function isArguments (object) { 1136 | return Object.prototype.toString.call(object) == '[object Arguments]'; 1137 | } 1138 | 1139 | function objEquiv (a, b) { 1140 | if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) 1141 | return false; 1142 | // an identical "prototype" property. 1143 | if (a.prototype !== b.prototype) return false; 1144 | //~~~I've managed to break Object.keys through screwy arguments passing. 1145 | // Converting to array solves the problem. 1146 | if (isArguments(a)) { 1147 | if (!isArguments(b)) { 1148 | return false; 1149 | } 1150 | a = pSlice.call(a); 1151 | b = pSlice.call(b); 1152 | return _deepEqual(a, b); 1153 | } 1154 | try{ 1155 | var ka = _keys(a), 1156 | kb = _keys(b), 1157 | key, i; 1158 | } catch (e) {//happens when one is a string literal and the other isn't 1159 | return false; 1160 | } 1161 | // having the same number of owned properties (keys incorporates hasOwnProperty) 1162 | if (ka.length != kb.length) 1163 | return false; 1164 | //the same set of keys (although not necessarily the same order), 1165 | ka.sort(); 1166 | kb.sort(); 1167 | //~~~cheap key test 1168 | for (i = ka.length - 1; i >= 0; i--) { 1169 | if (ka[i] != kb[i]) 1170 | return false; 1171 | } 1172 | //equivalent values for every corresponding key, and 1173 | //~~~possibly expensive deep test 1174 | for (i = ka.length - 1; i >= 0; i--) { 1175 | key = ka[i]; 1176 | if (!_deepEqual(a[key], b[key] )) 1177 | return false; 1178 | } 1179 | return true; 1180 | } 1181 | 1182 | // 8. The non-equivalence assertion tests for any deep inequality. 1183 | // assert.notDeepEqual(actual, expected, message_opt); 1184 | 1185 | assert.notDeepEqual = function notDeepEqual(actual, expected, message) { 1186 | if (_deepEqual(actual, expected)) { 1187 | fail(actual, expected, message, "notDeepEqual", assert.notDeepEqual); 1188 | } 1189 | }; 1190 | 1191 | // 9. The strict equality assertion tests strict equality, as determined by ===. 1192 | // assert.strictEqual(actual, expected, message_opt); 1193 | 1194 | assert.strictEqual = function strictEqual(actual, expected, message) { 1195 | if (actual !== expected) { 1196 | fail(actual, expected, message, "===", assert.strictEqual); 1197 | } 1198 | }; 1199 | 1200 | // 10. The strict non-equality assertion tests for strict inequality, as determined by !==. 1201 | // assert.notStrictEqual(actual, expected, message_opt); 1202 | 1203 | assert.notStrictEqual = function notStrictEqual(actual, expected, message) { 1204 | if (actual === expected) { 1205 | fail(actual, expected, message, "!==", assert.notStrictEqual); 1206 | } 1207 | }; 1208 | 1209 | function _throws (shouldThrow, block, err, message) { 1210 | var exception = null, 1211 | threw = false, 1212 | typematters = true; 1213 | 1214 | message = message || ""; 1215 | 1216 | //handle optional arguments 1217 | if (arguments.length == 3) { 1218 | if (typeof(err) == "string") { 1219 | message = err; 1220 | typematters = false; 1221 | } 1222 | } else if (arguments.length == 2) { 1223 | typematters = false; 1224 | } 1225 | 1226 | try { 1227 | block(); 1228 | } catch (e) { 1229 | threw = true; 1230 | exception = e; 1231 | } 1232 | 1233 | if (shouldThrow && !threw) { 1234 | fail( "Missing expected exception" 1235 | + (err && err.name ? " ("+err.name+")." : '.') 1236 | + (message ? " " + message : "") 1237 | ); 1238 | } 1239 | if (!shouldThrow && threw && typematters && exception instanceof err) { 1240 | fail( "Got unwanted exception" 1241 | + (err && err.name ? " ("+err.name+")." : '.') 1242 | + (message ? " " + message : "") 1243 | ); 1244 | } 1245 | if ((shouldThrow && threw && typematters && !(exception instanceof err)) || 1246 | (!shouldThrow && threw)) { 1247 | throw exception; 1248 | } 1249 | }; 1250 | 1251 | // 11. Expected to throw an error: 1252 | // assert.throws(block, Error_opt, message_opt); 1253 | 1254 | assert['throws'] = function(block, /*optional*/error, /*optional*/message) { 1255 | _throws.apply(this, [true].concat(pSlice.call(arguments))); 1256 | }; 1257 | 1258 | // EXTENSION! This is annoying to write outside this module. 1259 | assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) { 1260 | _throws.apply(this, [false].concat(pSlice.call(arguments))); 1261 | }; 1262 | 1263 | assert.ifError = function (err) { if (err) {throw err;}}; 1264 | })(assert); 1265 | (function(exports){ 1266 | /*! 1267 | * Nodeunit 1268 | * Copyright (c) 2010 Caolan McMahon 1269 | * MIT Licensed 1270 | * 1271 | * THIS FILE SHOULD BE BROWSER-COMPATIBLE JS! 1272 | * Only code on that line will be removed, its mostly to avoid requiring code 1273 | * that is node specific 1274 | */ 1275 | 1276 | /** 1277 | * Module dependencies 1278 | */ 1279 | 1280 | 1281 | 1282 | /** 1283 | * Creates assertion objects representing the result of an assert call. 1284 | * Accepts an object or AssertionError as its argument. 1285 | * 1286 | * @param {object} obj 1287 | * @api public 1288 | */ 1289 | 1290 | exports.assertion = function (obj) { 1291 | return { 1292 | method: obj.method || '', 1293 | message: obj.message || (obj.error && obj.error.message) || '', 1294 | error: obj.error, 1295 | passed: function () { 1296 | return !this.error; 1297 | }, 1298 | failed: function () { 1299 | return Boolean(this.error); 1300 | } 1301 | }; 1302 | }; 1303 | 1304 | /** 1305 | * Creates an assertion list object representing a group of assertions. 1306 | * Accepts an array of assertion objects. 1307 | * 1308 | * @param {Array} arr 1309 | * @param {Number} duration 1310 | * @api public 1311 | */ 1312 | 1313 | exports.assertionList = function (arr, duration) { 1314 | var that = arr || []; 1315 | that.failures = function () { 1316 | var failures = 0; 1317 | for (var i=0; i'; 1693 | }; 1694 | 1695 | 1696 | /** 1697 | * Run all tests within each module, reporting the results 1698 | * 1699 | * @param {Array} files 1700 | * @api public 1701 | */ 1702 | 1703 | exports.run = function (modules, options) { 1704 | var start = new Date().getTime(); 1705 | exports.addStyles(); 1706 | 1707 | var html = ''; 1708 | nodeunit.runModules(modules, { 1709 | moduleStart: function (name) { 1710 | html += '

' + name + '

'; 1711 | html += '
    '; 1712 | }, 1713 | testDone: function (name, assertions) { 1714 | if (!assertions.failures()) { 1715 | html += '
  1. ' + name + '
  2. '; 1716 | } 1717 | else { 1718 | html += '
  3. ' + name; 1719 | for (var i=0; i'; 1726 | } 1727 | html += '
    ';
    1728 |                         html += a.error.stack || a.error;
    1729 |                         html += '
    '; 1730 | } 1731 | }; 1732 | html += '
  4. '; 1733 | } 1734 | }, 1735 | moduleDone: function () { 1736 | html += '
'; 1737 | }, 1738 | done: function (assertions) { 1739 | var end = new Date().getTime(); 1740 | var duration = end - start; 1741 | if (assertions.failures()) { 1742 | html += '

FAILURES: ' + assertions.failures() + 1743 | '/' + assertions.length + ' assertions failed (' + 1744 | assertions.duration + 'ms)

'; 1745 | } 1746 | else { 1747 | html += '

OK: ' + assertions.length + 1748 | ' assertions (' + assertions.duration + 'ms)

'; 1749 | } 1750 | if (typeof document === 'undefined' && typeof print !== 'undefined') { 1751 | return print( html.replace(/
  • /g, '(/) ').replace(/
  • /g, '(X) ').replace(//g, "\n").replace(/<\/h\d>/g, "\n").replace(/
    /g, "\n ").replace(/<\/(li|div)>/g, "\n").replace(/<[^>]+?>/g, '') ); 1752 | } 1753 | document.body.innerHTML += html; 1754 | } 1755 | }); 1756 | }; 1757 | })(reporter); 1758 | nodeunit = core; 1759 | nodeunit.assert = assert; 1760 | nodeunit.reporter = reporter; 1761 | nodeunit.run = reporter.run; 1762 | return nodeunit; })(); --------------------------------------------------------------------------------