From a2bb5358e6dba32d00a56da252fc585245fa59d5 Mon Sep 17 00:00:00 2001 From: Michael Mathews Date: Sat, 7 May 2011 15:14:34 +0100 Subject: [PATCH] Squashed 'lib/Rhino-Require/' content from commit b34307a git-subtree-dir: lib/Rhino-Require git-subtree-split: b34307ac1534b153df376820a115efa3be76b092 --- README.md | 9 + src/require.js | 253 ++++ test/README.md | 5 + test/lib/jsmock.js | 377 ++++++ test/lib/nodeunit.js | 1762 +++++++++++++++++++++++++++ test/mock/bar/myModuleLib/bar.js | 2 + test/mock/bar/myModuleLib/extras.js | 1 + test/mock/bar/package.json | 10 + test/mock/foo.js | 0 test/mock/zop/index.js | 0 test/node_modules/baz/index.js | 0 test/node_modules/foobar.js | 0 test/run.js | 108 ++ 13 files changed, 2527 insertions(+) create mode 100644 README.md create mode 100644 src/require.js create mode 100644 test/README.md create mode 100644 test/lib/jsmock.js create mode 100644 test/lib/nodeunit.js create mode 100644 test/mock/bar/myModuleLib/bar.js create mode 100644 test/mock/bar/myModuleLib/extras.js create mode 100644 test/mock/bar/package.json create mode 100644 test/mock/foo.js create mode 100644 test/mock/zop/index.js create mode 100644 test/node_modules/baz/index.js create mode 100644 test/node_modules/foobar.js create mode 100644 test/run.js diff --git a/README.md b/README.md new file mode 100644 index 00000000..cd93e4d4 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +Rhino-Require +==== + +This is require() for rhino. + +A nodejs-compatible implementation of the commonjs require function, implemented +in code compatible with the Mozilla Rhino JavaScript engine. + +Written by Michael Mathews. Licensed as public domain. \ No newline at end of file diff --git a/src/require.js b/src/require.js new file mode 100644 index 00000000..64fe3c4f --- /dev/null +++ b/src/require.js @@ -0,0 +1,253 @@ +/* + Rhino-Require is Public Domain + + + The author or authors of this code dedicate any and all copyright interest + in this code to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and successors. We + intend this dedication to be an overt act of relinquishment in perpetuity of + all present and future rights to this code under copyright law. + */ + +(function(global) { + + var require = global.require = function(id) { /*debug*///console.log('require('+id+')'); + var moduleContent = '', + moduleUri; + + moduleUri = require.resolve(id); + moduleContent = ''; + + var file = new java.io.File(moduleUri); + try { + var scanner = new java.util.Scanner(file).useDelimiter("\\Z"); + moduleContent = String( scanner.next() ); + } + catch(e) { + throw 'Unable to read file at: '+moduleUri+', '+e; + } + + if (moduleContent) { + try { + var f = new Function('require', 'exports', 'module', moduleContent), + exports = require.cache[moduleUri] || {}, + module = { id: id, uri: moduleUri, exports: exports }; + + + require._root.unshift(moduleUri); + f.call({}, require, exports, module); + require._root.shift(); + } + catch(e) { + throw 'Unable to require source code from "' + moduleUri + '": ' + e.toSource(); + } + + exports = module.exports || exports; + require.cache[id] = exports; + } + else { + throw 'The requested module cannot be returned: no content for id: "' + id + '" in paths: ' + require.paths.join(', '); + } + + return exports; + } + require._root = ['']; + require.paths = []; + require.cache = {}; // cache module exports. Like: {id: exported} + + /** Given a module id, try to find the path to the associated module. + */ + require.resolve = function(id) { + // TODO: 1. load node core modules + + // 2. dot-relative module id, like './foo/bar' + var parts = id.match(/^(\.?\.\/|\/)(.+)$/), + isRelative = false, + isAbsolute = false, + basename = id; + + if (parts) { + isRelative = parts[1] === './' || parts[1] === '../'; + isAbsolute = parts[1] === '/'; + basename = parts[2]; + } + + if (typeof basename !== 'undefined') { + + if (isAbsolute) { + rootedId = id; + } + else { + var root = (isRelative? toDir(require._root[0] || '.') : '.'), + rootedId = (root + '/' + id).replace(/\/[^\/]+\/\.\.\//g, '/').replace(/\/\.\//g, '/'), + uri = ''; + } + + if ( uri = loadAsFile(rootedId) ) { } + else if ( uri = loadAsDir(rootedId) ) { } + else if ( uri = loadNodeModules(rootedId) ) { } + else if ( uri = nodeModulesPaths(rootedId, 'rhino_modules') ) { } + else if ( uri = nodeModulesPaths(rootedId, 'node_modules') ) { } + + if (uri !== '') return toAbsolute(uri); + + throw 'Require Error: Not found.'; + } + } + + /** Given a path, return the base directory of that path. + @example toDir('/foo/bar/somefile.js'); => '/foo/bar' + */ + function toDir(path) { + var file = new java.io.File(path); + + if (file.isDirectory()) { + return path; + } + + var parts = path.split(/[\\\/]/); + parts.pop(); + + return parts.join('/'); + } + + /** Returns true if the given path exists and is a file. + */ + function isFile(path) { + var file = new java.io.File(path); + + if (file.isFile()) { + return true; + } + + return false; + } + + /** Returns true if the given path exists and is a directory. + */ + function isDir(path) { + var file = new java.io.File(path); + + if (file.isDirectory()) { + return true; + } + + return false; + } + + /** Get the path of the current working directory + */ + function getCwd() { + return toDir( ''+new java.io.File('.').getAbsolutePath() ).replace(/\/\.$/, ''); + } + + function toAbsolute(relPath) { + absPath = ''+new java.io.File(relPath).getAbsolutePath(); + absPath = absPath.replace(/\/[^\/]+\/\.\.\//g, '/').replace(/\/\.\//g, '/'); + return absPath; + } + + /** Assume the id is a file, try to find it. + */ + function loadAsFile(id) { + if ( isFile(id) ) { return id; } + + if ( isFile(id+'.js') ) { return id+'.js'; } + + if ( isFile(id+'.node') ) { throw 'Require Error: .node files not supported'; } + } + + /** Assume the id is a directory, try to find a module file within it. + */ + function loadAsDir(id) { + if (!isDir(id)) { + return; + } + // look for the "main" property of the package.json file + if ( isFile(id+'/package.json') ) { + var packageJson = readFileSync(id+'/package.json', 'utf-8'); + eval( 'packageJson = '+ packageJson); + if (packageJson.hasOwnProperty('main')) { + var main = (id + '/' + packageJson.main).replace(/\/\.?\//g, '/'); + return require.resolve(main); + } + } + + if ( isFile(id+'/index.js') ) { + return id+'/index.js'; + } + } + + function loadNodeModules(id) { + var path, + uri; + for (var i = 0, len = require.paths.length; i < len; i++) { + path = require.paths[i]; + if (isDir(path)) { + path = (path + '/' + id).replace(/\/\.?\//g, '/'); + + uri = loadAsFile(path); + if (typeof uri !== 'undefined') { + return uri; + } + + uri = loadAsDir(path); + if (typeof uri !== 'undefined') { + return uri; + } + } + } + } + + function nodeModulesPaths(id, moduleFolder) { + var cwd = getCwd(), + dirs = cwd.split('/'), + dir, + path, + filename, + uri; + + while (dirs.length) { + dir = dirs.join('/'); + path = dir+'/'+moduleFolder; + + if ( isDir(path) ) { + filename = (path+'/'+id).replace(/\/\.?\//g, '/'); + + if ( uri = loadAsFile(filename) ) { + uri = uri.replace(cwd, '.'); + return uri; + } + + if ( uri = loadAsDir(filename) ) { + uri = uri.replace(cwd, '.'); + return uri; + } + } + + dirs.pop(); + } + } + + function readFileSync(filename, encoding, callback) { + if (typeof arguments[1] === 'function') { + encoding = null; + callback = arguments[1]; + } + + encoding = encoding || java.lang.System.getProperty('file.encoding'); + + try { + var content = new java.util.Scanner( + new java.io.File(filename), + encoding + ).useDelimiter("\\Z"); + + return String( content.next() ); + } + catch (e) { + return ''; + } + } + +})(this); \ No newline at end of file diff --git a/test/README.md b/test/README.md new file mode 100644 index 00000000..9d60454f --- /dev/null +++ b/test/README.md @@ -0,0 +1,5 @@ +To run the tests, change your current working directory to this test folder. + +Then: + + java -classpath js.jar org.mozilla.javascript.tools.shell.Main run.js diff --git a/test/lib/jsmock.js b/test/lib/jsmock.js new file mode 100644 index 00000000..3e78f555 --- /dev/null +++ b/test/lib/jsmock.js @@ -0,0 +1,377 @@ +/* +* JSMock 1.2.2, a mock object library for JavaScript +* Copyright (C) 2006 Justin DeWind +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +JSMock = { + extend: function(object) { + var mockControl = new MockControl(); + object.createMock = function(objectToMock) {return mockControl.createMock(objectToMock)}; + object.resetMocks = function() {mockControl.reset()}; + object.verifyMocks = function() {mockControl.verify()}; + + if(!object.tearDown) { + object.tearDown = function() { + object.verifyMocks(); + } + } + else if(object.tearDown.constructor == Function) { + object.__oldTearDown__ = object.tearDown; + object.tearDown = function() { + object.__oldTearDown__(); + object.verifyMocks(); + } + } + } +} + +function MockControl() { + this.__expectationMatcher = new ExpectationMatcher(); + this.__lastMock = null; + this.__lastCallName = null; +} + +MockControl.prototype = { + + createMock: function(objectToMock) { + var mock = { calls: [], expects: function() {this.__recording = true; return this}, __recording: false}; + mock.expect = mock.expects; + + if(objectToMock != null) { + + if( typeof(objectToMock) == 'function' ) { + this.__createMethods(objectToMock, mock); + this.__createMethods(new objectToMock(), mock); + } + else if( typeof(objectToMock) == 'object') { + this.__createMethods(objectToMock, mock); + } + else { + throw new Error("Cannot mock out a " + typeof(objectToMock)); + } + + } + + var self = this; + mock.addMockMethod = function(method) { self.__createMethod(self, mock, method); } + + return mock; + }, + + andReturn: function(returnValue) { + this.__verifyLastMockNotNull("Cannot set return value without an expectation"); + this.__initializeReturnExpectationForMock(); + this.__lastMock.calls[this.__lastCallName].push( function() { return returnValue; }); + }, + + andThrow: function(throwMsg) { + this.__verifyLastMockNotNull("Cannot throw error without an expectation"); + this.__initializeReturnExpectationForMock(); + this.__lastMock.calls[this.__lastCallName].push( function() { throw new Error(throwMsg); }); + }, + + andStub: function(block) { + this.__verifyLastMockNotNull("Cannot stub without an expectation"); + if( typeof(block) != 'function') { + throw new Error("Stub must be a function"); + } + this.__initializeReturnExpectationForMock(); + this.__lastMock.calls[this.__lastCallName].push( function() { return block.apply(this, arguments); }); + }, + + reset: function() { + this.__expectationMatcher.reset(); + }, + + verify: function() { + if(!this.__expectationMatcher.matches()) + { + discrepancy = this.__expectationMatcher.discrepancy(); + message = discrepancy.message; + method = discrepancy.behavior.method + formattedArgs = ArgumentFormatter.format(discrepancy.behavior.methodArguments); + this.__expectationMatcher.reset(); + throw new Error(message + ": " + method + "(" + formattedArgs + ")"); + } + else { + this.__expectationMatcher.reset(); + } + + }, + + __createMethods: function(object, mock) { + for( property in object ) { + if( this.__isPublicMethod(object, property) ) { + this.__createMethod( this, mock, property ); + } + } + }, + + __createMethod: function(control, mock, method) { + mock[method] = + function() { + if( mock.__recording ) { + control.__lastMock = mock; + control.__lastCallName = method; + control.__expectationMatcher.addExpectedMethodCall( mock, method, arguments ); + mock.__recording = false; + return control; + } + else { + control.__expectationMatcher.addActualMethodCall( mock, method, arguments ); + if( mock.calls[method] != null) { + returnValue = mock.calls[method].shift(); + if( typeof(returnValue) == 'function') { + return returnValue.apply(this, arguments); + } + } + } + } + }, + + __isPublicMethod: function(object, property) { + try { + return typeof(object[property]) == 'function' && property.charAt(0) != "_"; + } catch(e) { + return false; + } + }, + + __verifyLastMockNotNull: function(throwMsg) { + if(this.__lastMock == null) { + throw new Error(throwMsg); + } + }, + + __initializeReturnExpectationForMock: function() { + if(typeof(this.__lastMock.calls[this.__lastCallName]) == 'undefined') { + this.__lastMock.calls[this.__lastCallName] = []; + } + } +} + +function ExpectationMatcher() { + this.__expectationBehaviorList = []; + this.__actualBehaviorList = []; + this.__discrepancy = null; + +} + +ExpectationMatcher.prototype = { + addExpectedMethodCall: function(caller, method, methodArguments ) { + this.__expectationBehaviorList.push(new InvocationBehavior(caller, method, methodArguments)); + }, + + addActualMethodCall: function(caller, method, methodArguments ) { + this.__actualBehaviorList.push(new InvocationBehavior(caller, method, methodArguments)); + }, + + matches: function() { + var self = this; + var matches = true; + + this.__expectationBehaviorList.eachIndexForJsMock(function(index, expectedBehavior) { + var actualBehavior = (self.__actualBehaviorList.length > index) ? self.__actualBehaviorList[index] : null; + + if(matches) { + if( actualBehavior === null ) { + self.__discrepancy = new Discrepancy("Expected function not called", expectedBehavior); + matches = false; + } + else if( expectedBehavior.method != actualBehavior.method ) { + self.__discrepancy = new Discrepancy("Surprise call", actualBehavior); + matches = false; + } + else if( expectedBehavior.caller != actualBehavior.caller ) { + self.__discrepancy = new Discrepancy("Surprise call from unexpected caller", actualBehavior); + matches = false; + } + else if( !self.__matchArguments(expectedBehavior.methodArguments, actualBehavior.methodArguments) ) { + self.__discrepancy = new Discrepancy("Unexpected Arguments", actualBehavior); + matches = false; + } + } + }); + + if( this.__actualBehaviorList.length > this.__expectationBehaviorList.length && matches ) { + this.__discrepancy = new Discrepancy("Surprise call", this.__actualBehaviorList[this.__expectationBehaviorList.length]); + matches = false + } + + return matches; + }, + + reset: function() { + this.__expectationBehaviorList = []; + this.__actualBehaviorList = []; + this.__discrepancy = null; + }, + + discrepancy: function() { + return this.__discrepancy; + }, + + __matchArguments: function(expectedArgs, actualArgs) { + var expectedArray = this.__convertArgumentsToArray(expectedArgs); + var actualArray = this.__convertArgumentsToArray(actualArgs); + return ArgumentMatcher.matches(expectedArray, actualArray); + }, + + __convertArgumentsToArray: function(args) { + var convertedArguments = []; + + for(var i = 0; i < args.length; i++) { + convertedArguments[i] = args[i]; + } + + return convertedArguments; + } +} + +function InvocationBehavior(caller, method, methodArguments) { + this.caller = caller; + this.method = method; + this.methodArguments = methodArguments; +} + +function TypeOf(type) { + if(typeof(type) != 'function') + throw new Error("Can only take constructors"); + + this.type = type; +} + +TypeOf.isA = function(type) { return new TypeOf(type); }; + +ArgumentMatcher = { + + matches: function(expected, actual) { + return this.__delegateMatching(expected, actual); + }, + + __delegateMatching: function(expected, actual) { + if( expected == null ) { + return this.__match( expected, actual ); + } + else if( expected.constructor == TypeOf ) { + return this.__match(expected.type, actual.constructor); + } + else if( expected.constructor == Array ) { + return this.__matchArrays(expected, actual); + } + else { + return this.__match(expected, actual); + } + }, + + __match: function(expected, actual) { + return ( expected == actual ); + }, + + __matchArrays: function(expected, actual) { + if ( actual == null) + return false; + + if( actual.constructor != Array) + return false; + + if( expected.length != actual.length ) + return false; + + for(var i = 0; i < expected.length; i++ ) { + if( !this.__delegateMatching(expected[i], actual[i]) ) + return false; + } + + return true; + } +} + +function Discrepancy(message, behavior) { + if(behavior.constructor != InvocationBehavior) + throw new Error("The behavior can only be an InvocationBehavior object"); + + this.message = message; + this.behavior = behavior; +} + +ArgumentFormatter = { + + format: function(args) { + var formattedArgs = ""; + for(var i = 0; i < args.length; i++) { + if( args[i] == null ) { + formattedArgs += ( formattedArgs == "" ) ? "null" : ", " + "null"; + } + else if( args[i].constructor == TypeOf || args[i].constructor == Function) { + var func = ( args[i].constructor == TypeOf ) ? args[i].type : args[i]; + formattedArgs += ( formattedArgs == "" ) ? this.__formatFunction(func) : ", " + this.__formatFunction(func); + } + else if( typeof(args[i]) == "string" ) { + formattedArgs += ( formattedArgs == "" ) ? "\"" + args[i].toString() + "\"" : ", \"" + args[i].toString() + "\"" + } + else if( args[i].constructor == Array ) { + formattedArgs += ( formattedArgs == "" ) ? "[" + this.format(args[i]) + "]" : ", [" + this.format(args[i]) + "]"; + } + else { + formattedArgs += ( formattedArgs == "" ) ? args[i].toString() : ", " + args[i].toString(); + } + } + return formattedArgs; + }, + + __formatFunction: function(func) { + // Manual checking is done for internal/native functions + // since Safari will not display them correctly + // for the intended regex parsing. + + if(func == Array) { + return "Array"; + } else if(func == Date) { + return "Date"; + } else if(func == Object) { + return "Object"; + } else if(func == String) { + return "String"; + } else if(func == Function) { + return "Function"; + } else if(func == RegExp) { + return "RegExp"; + } else if(func == Error) { + return "Error"; + } else if(func == Number) { + return "Number"; + } else if(func == Boolean) { + return "Boolean"; + } + var formattedFunc = func.toString().match(/function (\w+)/); + + return ( formattedFunc == null ) ? "{{Closure}}" : formattedFunc[1]; + } + +} + +/* Helpers */ + +// Implemented each method with a unique name to avoid conflicting +// with other libraries that implement it. +Array.prototype.eachIndexForJsMock = function(block) { + for(var index = 0; index < this.length; index++) + { + block(index, this[index]); + } +} diff --git a/test/lib/nodeunit.js b/test/lib/nodeunit.js new file mode 100644 index 00000000..f1b1e467 --- /dev/null +++ b/test/lib/nodeunit.js @@ -0,0 +1,1762 @@ +/*! + * Nodeunit + * https://github.com/caolan/nodeunit + * Copyright (c) 2010 Caolan McMahon + * MIT Licensed + * + * json2.js + * http://www.JSON.org/json2.js + * Public Domain. + * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + */ +nodeunit = (function(){ +/* + http://www.JSON.org/json2.js + 2010-11-17 + + Public Domain. + + NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + + See http://www.JSON.org/js.html + + + This code should be minified before deployment. + See http://javascript.crockford.com/jsmin.html + + USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO + NOT CONTROL. + + + This file creates a global JSON object containing two methods: stringify + and parse. + + JSON.stringify(value, replacer, space) + value any JavaScript value, usually an object or array. + + replacer an optional parameter that determines how object + values are stringified for objects. It can be a + function or an array of strings. + + space an optional parameter that specifies the indentation + of nested structures. If it is omitted, the text will + be packed without extra whitespace. If it is a number, + it will specify the number of spaces to indent at each + level. If it is a string (such as '\t' or ' '), + it contains the characters used to indent at each level. + + This method produces a JSON text from a JavaScript value. + + When an object value is found, if the object contains a toJSON + method, its toJSON method will be called and the result will be + stringified. A toJSON method does not serialize: it returns the + value represented by the name/value pair that should be serialized, + or undefined if nothing should be serialized. The toJSON method + will be passed the key associated with the value, and this will be + bound to the value + + For example, this would serialize Dates as ISO strings. + + Date.prototype.toJSON = function (key) { + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + return this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z'; + }; + + You can provide an optional replacer method. It will be passed the + key and value of each member, with this bound to the containing + object. The value that is returned from your method will be + serialized. If your method returns undefined, then the member will + be excluded from the serialization. + + If the replacer parameter is an array of strings, then it will be + used to select the members to be serialized. It filters the results + such that only members with keys listed in the replacer array are + stringified. + + Values that do not have JSON representations, such as undefined or + functions, will not be serialized. Such values in objects will be + dropped; in arrays they will be replaced with null. You can use + a replacer function to replace those with JSON values. + JSON.stringify(undefined) returns undefined. + + The optional space parameter produces a stringification of the + value that is filled with line breaks and indentation to make it + easier to read. + + If the space parameter is a non-empty string, then that string will + be used for indentation. If the space parameter is a number, then + the indentation will be that many spaces. + + Example: + + text = JSON.stringify(['e', {pluribus: 'unum'}]); + // text is '["e",{"pluribus":"unum"}]' + + + text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); + // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' + + text = JSON.stringify([new Date()], function (key, value) { + return this[key] instanceof Date ? + 'Date(' + this[key] + ')' : value; + }); + // text is '["Date(---current time---)"]' + + + JSON.parse(text, reviver) + This method parses a JSON text to produce an object or array. + It can throw a SyntaxError exception. + + The optional reviver parameter is a function that can filter and + transform the results. It receives each of the keys and values, + and its return value is used instead of the original value. + If it returns what it received, then the structure is not modified. + If it returns undefined then the member is deleted. + + Example: + + // Parse the text. Values that look like ISO date strings will + // be converted to Date objects. + + myData = JSON.parse(text, function (key, value) { + var a; + if (typeof value === 'string') { + a = +/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); + if (a) { + return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], + +a[5], +a[6])); + } + } + return value; + }); + + myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { + var d; + if (typeof value === 'string' && + value.slice(0, 5) === 'Date(' && + value.slice(-1) === ')') { + d = new Date(value.slice(5, -1)); + if (d) { + return d; + } + } + return value; + }); + + + This is a reference implementation. You are free to copy, modify, or + redistribute. +*/ + +/*jslint evil: true, strict: false, regexp: false */ + +/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, + call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, + getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, + lastIndex, length, parse, prototype, push, replace, slice, stringify, + test, toJSON, toString, valueOf +*/ + + +// Create a JSON object only if one does not already exist. We create the +// methods in a closure to avoid creating global variables. + +if (!this.JSON) { + this.JSON = {}; +} + +(function () { + "use strict"; + + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + if (typeof Date.prototype.toJSON !== 'function') { + + Date.prototype.toJSON = function (key) { + + return isFinite(this.valueOf()) ? + this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z' : null; + }; + + String.prototype.toJSON = + Number.prototype.toJSON = + Boolean.prototype.toJSON = function (key) { + return this.valueOf(); + }; + } + + var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + gap, + indent, + meta = { // table of character substitutions + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"' : '\\"', + '\\': '\\\\' + }, + rep; + + + function quote(string) { + +// If the string contains no control characters, no quote characters, and no +// backslash characters, then we can safely slap some quotes around it. +// Otherwise we must also replace the offending characters with safe escape +// sequences. + + escapable.lastIndex = 0; + return escapable.test(string) ? + '"' + string.replace(escapable, function (a) { + var c = meta[a]; + return typeof c === 'string' ? c : + '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }) + '"' : + '"' + string + '"'; + } + + + function str(key, holder) { + +// Produce a string from holder[key]. + + var i, // The loop counter. + k, // The member key. + v, // The member value. + length, + mind = gap, + partial, + value = holder[key]; + +// If the value has a toJSON method, call it to obtain a replacement value. + + if (value && typeof value === 'object' && + typeof value.toJSON === 'function') { + value = value.toJSON(key); + } + +// If we were called with a replacer function, then call the replacer to +// obtain a replacement value. + + if (typeof rep === 'function') { + value = rep.call(holder, key, value); + } + +// What happens next depends on the value's type. + + switch (typeof value) { + case 'string': + return quote(value); + + case 'number': + +// JSON numbers must be finite. Encode non-finite numbers as null. + + return isFinite(value) ? String(value) : 'null'; + + case 'boolean': + case 'null': + +// If the value is a boolean or null, convert it to a string. Note: +// typeof null does not produce 'null'. The case is included here in +// the remote chance that this gets fixed someday. + + return String(value); + +// If the type is 'object', we might be dealing with an object or an array or +// null. + + case 'object': + +// Due to a specification blunder in ECMAScript, typeof null is 'object', +// so watch out for that case. + + if (!value) { + return 'null'; + } + +// Make an array to hold the partial results of stringifying this object value. + + gap += indent; + partial = []; + +// Is the value an array? + + if (Object.prototype.toString.apply(value) === '[object Array]') { + +// The value is an array. Stringify every element. Use null as a placeholder +// for non-JSON values. + + length = value.length; + for (i = 0; i < length; i += 1) { + partial[i] = str(i, value) || 'null'; + } + +// Join all of the elements together, separated with commas, and wrap them in +// brackets. + + v = partial.length === 0 ? '[]' : + gap ? '[\n' + gap + + partial.join(',\n' + gap) + '\n' + + mind + ']' : + '[' + partial.join(',') + ']'; + gap = mind; + return v; + } + +// If the replacer is an array, use it to select the members to be stringified. + + if (rep && typeof rep === 'object') { + length = rep.length; + for (i = 0; i < length; i += 1) { + k = rep[i]; + if (typeof k === 'string') { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } else { + +// Otherwise, iterate through all of the keys in the object. + + for (k in value) { + if (Object.hasOwnProperty.call(value, k)) { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } + +// Join all of the member texts together, separated with commas, +// and wrap them in braces. + + v = partial.length === 0 ? '{}' : + gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + + mind + '}' : '{' + partial.join(',') + '}'; + gap = mind; + return v; + } + } + +// If the JSON object does not yet have a stringify method, give it one. + + if (typeof JSON.stringify !== 'function') { + JSON.stringify = function (value, replacer, space) { + +// The stringify method takes a value and an optional replacer, and an optional +// space parameter, and returns a JSON text. The replacer can be a function +// that can replace values, or an array of strings that will select the keys. +// A default replacer method can be provided. Use of the space parameter can +// produce text that is more easily readable. + + var i; + gap = ''; + indent = ''; + +// If the space parameter is a number, make an indent string containing that +// many spaces. + + if (typeof space === 'number') { + for (i = 0; i < space; i += 1) { + indent += ' '; + } + +// If the space parameter is a string, it will be used as the indent string. + + } else if (typeof space === 'string') { + indent = space; + } + +// If there is a replacer, it must be a function or an array. +// Otherwise, throw an error. + + rep = replacer; + if (replacer && typeof replacer !== 'function' && + (typeof replacer !== 'object' || + typeof replacer.length !== 'number')) { + throw new Error('JSON.stringify'); + } + +// Make a fake root object containing our value under the key of ''. +// Return the result of stringifying the value. + + return str('', {'': value}); + }; + } + + +// If the JSON object does not yet have a parse method, give it one. + + if (typeof JSON.parse !== 'function') { + JSON.parse = function (text, reviver) { + +// The parse method takes a text and an optional reviver function, and returns +// a JavaScript value if the text is a valid JSON text. + + var j; + + function walk(holder, key) { + +// The walk method is used to recursively walk the resulting structure so +// that modifications can be made. + + var k, v, value = holder[key]; + if (value && typeof value === 'object') { + for (k in value) { + if (Object.hasOwnProperty.call(value, k)) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } else { + delete value[k]; + } + } + } + } + return reviver.call(holder, key, value); + } + + +// Parsing happens in four stages. In the first stage, we replace certain +// Unicode characters with escape sequences. JavaScript handles many characters +// incorrectly, either silently deleting them, or treating them as line endings. + + text = String(text); + cx.lastIndex = 0; + if (cx.test(text)) { + text = text.replace(cx, function (a) { + return '\\u' + + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }); + } + +// In the second stage, we run the text against regular expressions that look +// for non-JSON patterns. We are especially concerned with '()' and 'new' +// because they can cause invocation, and '=' because it can cause mutation. +// But just to be safe, we want to reject all unexpected forms. + +// We split the second stage into 4 regexp operations in order to work around +// crippling inefficiencies in IE's and Safari's regexp engines. First we +// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we +// replace all simple value tokens with ']' characters. Third, we delete all +// open brackets that follow a colon or comma or that begin the text. Finally, +// we look to see that the remaining characters are only whitespace or ']' or +// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. + + if (/^[\],:{}\s]*$/ +.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') +.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') +.replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { + +// In the third stage we use the eval function to compile the text into a +// JavaScript structure. The '{' operator is subject to a syntactic ambiguity +// in JavaScript: it can begin a block or an object literal. We wrap the text +// in parens to eliminate the ambiguity. + + j = eval('(' + text + ')'); + +// In the optional fourth stage, we recursively walk the new structure, passing +// each name/value pair to a reviver function for possible transformation. + + return typeof reviver === 'function' ? + walk({'': j}, '') : j; + } + +// If the text is not JSON parseable, then a SyntaxError is thrown. + + throw new SyntaxError('JSON.parse'); + }; + } +}()); +var assert = {}; +var types = {}; +var core = {}; +var nodeunit = {}; +var reporter = {}; +(function(){ + + var async = {}; + + // global on the server, window in the browser + var root = this; + var previous_async = root.async; + + if(typeof module !== 'undefined' && module.exports) module.exports = async; + else root.async = async; + + async.noConflict = function(){ + root.async = previous_async; + return async; + }; + + //// cross-browser compatiblity functions //// + + var _forEach = function(arr, iterator){ + if(arr.forEach) return arr.forEach(iterator); + for(var i=0; i b ? 1 : 0; + }), function(x){return x.value;})); + }) + }; + + async.auto = function(tasks, callback){ + callback = callback || function(){}; + var keys = _keys(tasks); + if(!keys.length) return callback(null); + + var completed = []; + + var listeners = []; + var addListener = function(fn){ + listeners.unshift(fn); + }; + var removeListener = function(fn){ + for(var i=0; i +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the 'Software'), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +var pSlice = Array.prototype.slice; + +// 1. The assert module provides functions that throw +// AssertionError's when particular conditions are not met. The +// assert module must conform to the following interface. + +var assert = exports; + +// 2. The AssertionError is defined in assert. +// new assert.AssertionError({message: message, actual: actual, expected: expected}) + +assert.AssertionError = function AssertionError (options) { + this.name = "AssertionError"; + this.message = options.message; + this.actual = options.actual; + this.expected = options.expected; + this.operator = options.operator; + var stackStartFunction = options.stackStartFunction || fail; + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, stackStartFunction); + } +}; +// code from util.inherits in node +assert.AssertionError.super_ = Error; + + +// EDITED FOR BROWSER COMPATIBILITY: replaced Object.create call +// TODO: test what effect this may have +var ctor = function () { this.constructor = assert.AssertionError; }; +ctor.prototype = Error.prototype; +assert.AssertionError.prototype = new ctor(); + + +assert.AssertionError.prototype.toString = function() { + if (this.message) { + return [this.name+":", this.message].join(' '); + } else { + return [ this.name+":" + , JSON.stringify(this.expected ) + , this.operator + , JSON.stringify(this.actual) + ].join(" "); + } +}; + +// assert.AssertionError instanceof Error + +assert.AssertionError.__proto__ = Error.prototype; + +// At present only the three keys mentioned above are used and +// understood by the spec. Implementations or sub modules can pass +// other keys to the AssertionError's constructor - they will be +// ignored. + +// 3. All of the following functions must throw an AssertionError +// when a corresponding condition is not met, with a message that +// may be undefined if not provided. All assertion methods provide +// both the actual and expected values to the assertion error for +// display purposes. + +function fail(actual, expected, message, operator, stackStartFunction) { + throw new assert.AssertionError({ + message: message, + actual: actual, + expected: expected, + operator: operator, + stackStartFunction: stackStartFunction + }); +} + +// EXTENSION! allows for well behaved errors defined elsewhere. +assert.fail = fail; + +// 4. Pure assertion tests whether a value is truthy, as determined +// by !!guard. +// assert.ok(guard, message_opt); +// This statement is equivalent to assert.equal(true, guard, +// message_opt);. To test strictly for the value true, use +// assert.strictEqual(true, guard, message_opt);. + +assert.ok = function ok(value, message) { + if (!!!value) fail(value, true, message, "==", assert.ok); +}; + +// 5. The equality assertion tests shallow, coercive equality with +// ==. +// assert.equal(actual, expected, message_opt); + +assert.equal = function equal(actual, expected, message) { + if (actual != expected) fail(actual, expected, message, "==", assert.equal); +}; + +// 6. The non-equality assertion tests for whether two objects are not equal +// with != assert.notEqual(actual, expected, message_opt); + +assert.notEqual = function notEqual(actual, expected, message) { + if (actual == expected) { + fail(actual, expected, message, "!=", assert.notEqual); + } +}; + +// 7. The equivalence assertion tests a deep equality relation. +// assert.deepEqual(actual, expected, message_opt); + +assert.deepEqual = function deepEqual(actual, expected, message) { + if (!_deepEqual(actual, expected)) { + fail(actual, expected, message, "deepEqual", assert.deepEqual); + } +}; + +function _deepEqual(actual, expected) { + // 7.1. All identical values are equivalent, as determined by ===. + if (actual === expected) { + return true; + + } else if (Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) { + if (actual.length != expected.length) return false; + + for (var i = 0; i < actual.length; i++) { + if (actual[i] !== expected[i]) return false; + } + + return true; + + // 7.2. If the expected value is a Date object, the actual value is + // equivalent if it is also a Date object that refers to the same time. + } else if (actual instanceof Date && expected instanceof Date) { + return actual.getTime() === expected.getTime(); + + // 7.3. Other pairs that do not both pass typeof value == "object", + // equivalence is determined by ==. + } else if (typeof actual != 'object' && typeof expected != 'object') { + return actual == expected; + + // 7.4. For all other Object pairs, including Array objects, equivalence is + // determined by having the same number of owned properties (as verified + // with Object.prototype.hasOwnProperty.call), the same set of keys + // (although not necessarily the same order), equivalent values for every + // corresponding key, and an identical "prototype" property. Note: this + // accounts for both named and indexed properties on Arrays. + } else { + return objEquiv(actual, expected); + } +} + +function isUndefinedOrNull (value) { + return value === null || value === undefined; +} + +function isArguments (object) { + return Object.prototype.toString.call(object) == '[object Arguments]'; +} + +function objEquiv (a, b) { + if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) + return false; + // an identical "prototype" property. + if (a.prototype !== b.prototype) return false; + //~~~I've managed to break Object.keys through screwy arguments passing. + // Converting to array solves the problem. + if (isArguments(a)) { + if (!isArguments(b)) { + return false; + } + a = pSlice.call(a); + b = pSlice.call(b); + return _deepEqual(a, b); + } + try{ + var ka = _keys(a), + kb = _keys(b), + key, i; + } catch (e) {//happens when one is a string literal and the other isn't + return false; + } + // having the same number of owned properties (keys incorporates hasOwnProperty) + if (ka.length != kb.length) + return false; + //the same set of keys (although not necessarily the same order), + ka.sort(); + kb.sort(); + //~~~cheap key test + for (i = ka.length - 1; i >= 0; i--) { + if (ka[i] != kb[i]) + return false; + } + //equivalent values for every corresponding key, and + //~~~possibly expensive deep test + for (i = ka.length - 1; i >= 0; i--) { + key = ka[i]; + if (!_deepEqual(a[key], b[key] )) + return false; + } + return true; +} + +// 8. The non-equivalence assertion tests for any deep inequality. +// assert.notDeepEqual(actual, expected, message_opt); + +assert.notDeepEqual = function notDeepEqual(actual, expected, message) { + if (_deepEqual(actual, expected)) { + fail(actual, expected, message, "notDeepEqual", assert.notDeepEqual); + } +}; + +// 9. The strict equality assertion tests strict equality, as determined by ===. +// assert.strictEqual(actual, expected, message_opt); + +assert.strictEqual = function strictEqual(actual, expected, message) { + if (actual !== expected) { + fail(actual, expected, message, "===", assert.strictEqual); + } +}; + +// 10. The strict non-equality assertion tests for strict inequality, as determined by !==. +// assert.notStrictEqual(actual, expected, message_opt); + +assert.notStrictEqual = function notStrictEqual(actual, expected, message) { + if (actual === expected) { + fail(actual, expected, message, "!==", assert.notStrictEqual); + } +}; + +function _throws (shouldThrow, block, err, message) { + var exception = null, + threw = false, + typematters = true; + + message = message || ""; + + //handle optional arguments + if (arguments.length == 3) { + if (typeof(err) == "string") { + message = err; + typematters = false; + } + } else if (arguments.length == 2) { + typematters = false; + } + + try { + block(); + } catch (e) { + threw = true; + exception = e; + } + + if (shouldThrow && !threw) { + fail( "Missing expected exception" + + (err && err.name ? " ("+err.name+")." : '.') + + (message ? " " + message : "") + ); + } + if (!shouldThrow && threw && typematters && exception instanceof err) { + fail( "Got unwanted exception" + + (err && err.name ? " ("+err.name+")." : '.') + + (message ? " " + message : "") + ); + } + if ((shouldThrow && threw && typematters && !(exception instanceof err)) || + (!shouldThrow && threw)) { + throw exception; + } +}; + +// 11. Expected to throw an error: +// assert.throws(block, Error_opt, message_opt); + +assert['throws'] = function(block, /*optional*/error, /*optional*/message) { + _throws.apply(this, [true].concat(pSlice.call(arguments))); +}; + +// EXTENSION! This is annoying to write outside this module. +assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) { + _throws.apply(this, [false].concat(pSlice.call(arguments))); +}; + +assert.ifError = function (err) { if (err) {throw err;}}; +})(assert); +(function(exports){ +/*! + * Nodeunit + * Copyright (c) 2010 Caolan McMahon + * MIT Licensed + * + * THIS FILE SHOULD BE BROWSER-COMPATIBLE JS! + * Only code on that line will be removed, its mostly to avoid requiring code + * that is node specific + */ + +/** + * Module dependencies + */ + + + +/** + * Creates assertion objects representing the result of an assert call. + * Accepts an object or AssertionError as its argument. + * + * @param {object} obj + * @api public + */ + +exports.assertion = function (obj) { + return { + method: obj.method || '', + message: obj.message || (obj.error && obj.error.message) || '', + error: obj.error, + passed: function () { + return !this.error; + }, + failed: function () { + return Boolean(this.error); + } + }; +}; + +/** + * Creates an assertion list object representing a group of assertions. + * Accepts an array of assertion objects. + * + * @param {Array} arr + * @param {Number} duration + * @api public + */ + +exports.assertionList = function (arr, duration) { + var that = arr || []; + that.failures = function () { + var failures = 0; + for (var i=0; i'; +}; + + +/** + * Run all tests within each module, reporting the results + * + * @param {Array} files + * @api public + */ + +exports.run = function (modules, options) { + var start = new Date().getTime(); + exports.addStyles(); + + var html = ''; + nodeunit.runModules(modules, { + moduleStart: function (name) { + html += '

' + name + '

'; + html += '
    '; + }, + testDone: function (name, assertions) { + if (!assertions.failures()) { + html += '
  1. ' + name + '
  2. '; + } + else { + html += '
  3. ' + name; + for (var i=0; i'; + } + html += '
    ';
    +                        html += a.error.stack || a.error;
    +                        html += '
    '; + } + }; + html += '
  4. '; + } + }, + moduleDone: function () { + html += '
'; + }, + done: function (assertions) { + var end = new Date().getTime(); + var duration = end - start; + if (assertions.failures()) { + html += '

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

'; + } + else { + html += '

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

'; + } + if (typeof document === 'undefined' && typeof print !== 'undefined') { + return print( html.replace(/
  • /g, '\033[1;032m√ \033[0m').replace(/
  • /g, '\033[1;031mX \033[0m').replace(//g, "\n").replace(/<\/h\d>/g, "\n").replace(/
    /g, "\n ").replace(/<\/(li|div)>/g, "\n").replace(/<[^>]+?>/g, '') ); + } + document.body.innerHTML += html; + } + }); +}; +})(reporter); +nodeunit = core; +nodeunit.assert = assert; +nodeunit.reporter = reporter; +nodeunit.run = reporter.run; +return nodeunit; })(); \ No newline at end of file diff --git a/test/mock/bar/myModuleLib/bar.js b/test/mock/bar/myModuleLib/bar.js new file mode 100644 index 00000000..7875591b --- /dev/null +++ b/test/mock/bar/myModuleLib/bar.js @@ -0,0 +1,2 @@ +exports.name = 'bar'; +exports.extras = require('./extras'); \ No newline at end of file diff --git a/test/mock/bar/myModuleLib/extras.js b/test/mock/bar/myModuleLib/extras.js new file mode 100644 index 00000000..9d62c3e9 --- /dev/null +++ b/test/mock/bar/myModuleLib/extras.js @@ -0,0 +1 @@ +exports.name = 'extras'; \ No newline at end of file diff --git a/test/mock/bar/package.json b/test/mock/bar/package.json new file mode 100644 index 00000000..6ab8a81f --- /dev/null +++ b/test/mock/bar/package.json @@ -0,0 +1,10 @@ +{ + "name": "my-test-package", + "version": "1.2.3", + "description": "Blah blah blah", + "keywords": [ + "package", + "example" + ], + "main": "./myModuleLib/bar.js" +} \ No newline at end of file diff --git a/test/mock/foo.js b/test/mock/foo.js new file mode 100644 index 00000000..e69de29b diff --git a/test/mock/zop/index.js b/test/mock/zop/index.js new file mode 100644 index 00000000..e69de29b diff --git a/test/node_modules/baz/index.js b/test/node_modules/baz/index.js new file mode 100644 index 00000000..e69de29b diff --git a/test/node_modules/foobar.js b/test/node_modules/foobar.js new file mode 100644 index 00000000..e69de29b diff --git a/test/run.js b/test/run.js new file mode 100644 index 00000000..0ceb994f --- /dev/null +++ b/test/run.js @@ -0,0 +1,108 @@ +// USAGE: java -classpath ~/Scripts/js.jar org.mozilla.javascript.tools.shell.Main test.js + +load('./lib/nodeunit.js'); +//load('lib/jsmock.js'); + +load('../src/require.js'); + +module = typeof module === 'undefined'? {} : module; +var test = module.exports = { + 'Basic tests.': { + 'The require function should be defined.': function(t) { + t.expect(1); + t.equal( typeof require, 'function' ); + t.done(); + } + }, + 'The require.resolve function.': { + 'The require.resolve function should be defined.': function(t) { + t.expect(1); + t.equal( typeof require.resolve, 'function' ); + t.done(); + }, + 'When an id starts with "./" it should resolve relative to the current working directory.': function(t) { + t.expect(1); + t.equal( require.resolve( './mock/foo'), toAbsolute('./mock/foo.js') ); + t.done(); + }, + 'When an id starts with "./" it should resolve relative to the current running module.': function(t) { + t.expect(1); + require._root.unshift('./mock/bar.js'); + t.equal( require.resolve('./foo'), toAbsolute('./mock/foo.js') ); + require._root.shift(); + t.done(); + }, + 'When an id does not start with "./" it should resolve relative to the cwd.': function(t) { + t.expect(1); + require._root.unshift('blah/one/two.js'); + t.equal( require.resolve('mock/foo'), toAbsolute('./mock/foo.js') ); + require._root.shift(); + t.done(); + } + }, + 'Resolve from package.json.': { + 'The require.resolve function should use the "main" property from package.json.': function(t) { + t.expect(1); + t.equal( require.resolve('./mock/bar'), toAbsolute('./mock/bar/myModuleLib/bar.js') ); + t.done(); + } + }, + 'Resolve from index file.': { + 'The require.resolve function should use the "index.js" file.': function(t) { + t.expect(1); + t.equal( require.resolve('./mock/zop'), toAbsolute('./mock/zop/index.js') ); + t.done(); + } + }, + 'Resolve from require.paths.': { + 'The require.resolve function should use the require.paths values.': function(t) { + t.expect(1); + require.paths.push('./mock'); + t.equal( require.resolve('foo'), toAbsolute('./mock/foo.js') ); + require.paths.pop(); + + t.done(); + } + }, + 'Resolve from node_modules.': { + 'The require.resolve function should use the node_modules dir.': function(t) { + t.expect(1); + t.equal( require.resolve('foobar'), toAbsolute('./node_modules/foobar.js') ); + t.done(); + }, + 'The require.resolve function should look for index in node_modules dir.': function(t) { + t.expect(1); + t.equal( require.resolve('baz'), toAbsolute('./node_modules/baz/index.js') ); + t.done(); + } + }, + 'Require from package.json.': { + 'The required module should be returned when it is listed in package.json.': function(t) { + t.expect(3); + var bar = require('mock/bar'); + t.equal( typeof bar, 'object' ); + t.equal( bar.name, 'bar' ); + t.equal( bar.extras.name, 'extras' ); + t.done(); + }, + 'The required dot-relative module should be returned when it is listed in package.json.': function(t) { + t.expect(2); + var bar = require('./mock/bar'); + t.equal( typeof bar, 'object' ); + t.equal( bar.name, 'bar' ); + t.done(); + } + } +}; + +var cwd = (typeof __dirname === 'undefined')? ''+new java.io.File('.').getAbsolutePath() : __dirname; +function toAbsolute(relPath) { + if ( /^\//.test(relPath) ) return relPath; + + relPath = relPath.replace(/^\.\//, ''); + var absPath = cwd + '/' + relPath; + absPath = absPath.replace(/\/[^\/]+\/\.\.\//g, '/').replace(/\/\.\//g, '/'); + return absPath; +} + +nodeunit.run(test); \ No newline at end of file