Testing Framework Upgrade Part I

This first part of the testing framework upgrade:

- enhances the jsdoc script to allow switching to debug mode from the command line
- uses Jasmine to run tests
- adds flexibility to run tests from multiple root directories
- does automatic test discovery, so test files can just be created, dropped in an appropriate folder, and run without having to explicity add it to the test runner
- cleans up the test directory layout
- incorporates env.rhino.js which should make it easier to test templates
- is incomplete: this is just a savepoint.  About 1/3 of the tests have been converted.  The rest are still run through the old testrunner
This commit is contained in:
Jannon 2012-04-30 17:39:50 -07:00
parent e35ddfab69
commit 2d73440b3d
130 changed files with 18486 additions and 820 deletions

21
jsdoc
View File

@ -4,6 +4,23 @@
SOURCE="$0"
while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done
BASEDIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
java -classpath ${BASEDIR}/lib/js.jar org.mozilla.javascript.tools.shell.Main -modules ${BASEDIR}/node_modules -modules ${BASEDIR}/rhino_modules -modules ${BASEDIR} ${BASEDIR}/jsdoc.js --dirname=${BASEDIR} $@
#java -classpath ${BASEDIR}/lib/js.jar org.mozilla.javascript.tools.debugger.Main -debug -modules ${BASEDIR}/node_modules -modules ${BASEDIR}/rhino_modules -modules ${BASEDIR} ${BASEDIR}/jsdoc.js --dirname=${BASEDIR} $@
if test $1 = "--debug"
then
CMD="org.mozilla.javascript.tools.debugger.Main -debug"
shift
else
CMD="org.mozilla.javascript.tools.shell.Main"
fi
#Conditionally execute different command lines depending on whether we're running tests or not
if test $1 = "-T"
then
echo "Running Tests"
java -classpath ${BASEDIR}/lib/js.jar ${CMD} -opt -1 -modules ${BASEDIR}/node_modules -modules ${BASEDIR}/rhino_modules -modules ${BASEDIR} ${BASEDIR}/jsdoc.js --dirname=${BASEDIR} $@
else
echo "Running Normal"
java -classpath ${BASEDIR}/lib/js.jar ${CMD} -modules ${BASEDIR}/node_modules -modules ${BASEDIR}/rhino_modules -modules ${BASEDIR} ${BASEDIR}/jsdoc.js --dirname=${BASEDIR} $@
fi
#java -classpath ${BASEDIR}/lib/js.jar ${CMD} -modules ${BASEDIR}/node_modules -modules ${BASEDIR}/rhino_modules -modules ${BASEDIR} ${BASEDIR}/jsdoc.js --dirname=${BASEDIR} $@

View File

@ -11,13 +11,13 @@ console = {
log: function(/*...*/) {
var args = Array.prototype.slice.call(arguments, 0),
dumper = dumper || require('jsdoc/util/dumper');
for (var i = 0, len = args.length; i < len; i++) {
if (typeof args[i] !== 'string') {
args[i] = dumper.dump(args[i]);
}
}
print( args.join(' ') );
}
};
@ -33,4 +33,3 @@ process = {
},
argv: [__dirname + '/jsdoc.js'].concat(Array.prototype.slice.call(arguments, 0))
};

107
node_modules/common/args.js generated vendored
View File

@ -14,22 +14,24 @@
*/
exports.ArgParser = function() {
this._options = [];
}
this._shortNameIndex = {};
this._longNameIndex = {};
};
exports.ArgParser.prototype._getOptionByShortName = function(name) {
for (var i = this._options.length; i--;) {
if (this._options[i].shortName === name) { return this._options[i]; }
if (this._shortNameIndex.hasOwnProperty(name)) {
return this._options[this._shortNameIndex[name]];
}
return null;
}
};
exports.ArgParser.prototype._getOptionByLongName = function(name) {
for (var i = this._options.length; i--;) {
if (this._options[i].longName === name) { return this._options[i]; }
}
return null;
}
if (this._longNameIndex.hasOwnProperty(name)) {
return this._options[this._longNameIndex[name]];
}
return null;
};
/**
* Provide information about a legal option.
* @param {character} shortName The short name of the option, entered like: -T.
@ -40,39 +42,47 @@
* myParser.addOption('t', 'template', true, 'The path to the template.');
* myParser.addOption('h', 'help', false, 'Show the help message.');
*/
exports.ArgParser.prototype.addOption = function(shortName, longName, hasValue, helpText) {
this._options.push({shortName: shortName, longName: longName, hasValue: hasValue, helpText: helpText});
exports.ArgParser.prototype.addOption = function(shortName, longName, hasValue, helpText, canHaveMultiple) {
this._options.push({shortName: shortName, longName: longName, hasValue: hasValue, helpText: helpText, canHaveMultiple: (canHaveMultiple || false)});
if (shortName) {
this._shortNameIndex[shortName] = this._options.length - 1;
}
if (longName) {
this._longNameIndex[longName] = this._options.length - 1;
}
};
/**
Generate a summary of all the options with corresponding help text.
@returns {string}
*/
exports.ArgParser.prototype.help = function() {
var help = 'OPTIONS:\n',
option;
var helpArr = ['OPTIONS:'],
option, optionHelp;
for (var i = 0, leni = this._options.length; i < leni; i++) {
option = this._options[i];
optionHelp = '\t';
if (option.shortName) {
help += '-' + option.shortName + (option.longName? ' or ' : '');
optionHelp += '-' + option.shortName + (option.longName ? ', ' : '');
}
if (option.longName) {
help += '--' + option.longName;
optionHelp += '--' + option.longName;
}
if (option.hasValue) {
help += ' <value>';
optionHelp += ' <value>';
}
help += ' ' + option.helpText + '\n';
optionHelp += '\t\t' + option.helpText;
helpArr.push(optionHelp);
}
return help;
return helpArr.join('\n');
};
/**
Get the options.
@param {Array.<string>} args An array, like ['-x', 'hello']
@ -82,22 +92,22 @@
provided, or `true` if the option accepts no value.
*/
exports.ArgParser.prototype.parse = function(args, defaults) {
var result = defaults || {};
var util = require('common/util'),
result = defaults && util.mixin({}, defaults) || {};
result._ = [];
for (var i = 0, leni = args.length; i < leni; i++) {
var arg = '' + args[i],
next = (i < leni-1)? '' + args[i+1] : null,
option,
shortName,
shortName = null,
longName,
name,
value = null;
// like -t
if (arg.charAt(0) === '-') {
// like: --template
if (arg.charAt(1) === '-') {
name = longName = arg.slice(2);
@ -107,15 +117,15 @@
name = shortName = arg.slice(1);
option = this._getOptionByShortName(shortName);
}
if (option === null) {
throw new Error( 'Unknown command line option found: ' + name );
}
if (option.hasValue) {
value = next;
i++;
if (value === null || value.charAt(0) === '-') {
throw new Error( 'Command line option requires a value: ' + name );
}
@ -123,18 +133,29 @@
else {
value = true;
}
if (option.longName && shortName) {
name = option.longName;
}
result[name] = value;
// Allow for multiple options of the same type to be present
if (option.canHaveMultiple && result.hasOwnProperty(name)) {
var val = result[name];
if (val instanceof Array) {
val.push(value);
} else {
result[name] = [val, value];
}
}
else {
result[name] = value;
}
}
else {
result._.push(arg);
}
}
return result;
}
};
})();

View File

@ -7,12 +7,17 @@ exports.readFileSync = function(filename, encoding) {
var readdirSync = exports.readdirSync = function(path) {
var dir,
files;
dir = new java.io.File(path);
if (!dir.directory) { return [String(dir)]; }
files = dir.list();
//Convert files to Javascript strings so they play nice with node modules
files = files.map(function(fileName) {
return String(fileName);
});
return files;
};
@ -24,20 +29,20 @@ var ls = exports.ls = function(dir, recurse, _allFiles, _path) {
_allFiles = [];
_path = [dir];
}
if (_path.length === 0) { return _allFiles; }
if (typeof recurse === 'undefined') { recurse = 1; }
if ( stat(dir).isFile(dir) ) {
files = [dir];
}
else {
files = readdirSync(dir);
}
for (var f = 0, lenf = files.length; f < lenf; f++) {
file = String(files[f]);
if (file.match(/^\.[^\.\/\\]/)) { continue; } // skip dot files
if ((new java.io.File(_path.join('/') + '/' + file)).list()) { // it's a directory
@ -48,7 +53,7 @@ var ls = exports.ls = function(dir, recurse, _allFiles, _path) {
}
_path.pop();
}
else { // it's a file
else { // it's a file
_allFiles.push(
(_path.join('/') + '/' + file).replace(/[\/\\]+/g, '/')
);
@ -58,21 +63,21 @@ var ls = exports.ls = function(dir, recurse, _allFiles, _path) {
return _allFiles;
};
var stat = exports.stat = function(path, encoding) {
var f = new java.io.File(path)
var stat = exports.stat = exports.statSync = function(path, encoding) {
var f = new java.io.File(path);
return {
isFile: function() {
return f.isFile();
},
isDir: function() {
isDirectory: function() {
return f.isDirectory();
}
}
};
};
exports.mkPath = function(/**Array*/ path) {
if (path.constructor != Array) path = path.split(/[\\\/]/);
if (path.constructor != Array){path = path.split(/[\\\/]/);}
var make = "";
for (var i = 0, l = path.length; i < l; i++) {
make += path[i] + '/';
@ -104,25 +109,25 @@ function exists(path) {
var toDir = exports.toDir = function(path) {
var f = new java.io.File(path);
if (f.isDirectory()){
return path;
}
var parts = path.split(/[\\\/]/);
parts.pop();
return parts.join('/');
};
exports.copyFile = function(inFile, outDir, fileName) {
if (fileName == null) fileName = toFile(inFile);
if (fileName == null){fileName = toFile(inFile);}
outDir = toDir(outDir);
var inFile = new java.io.File(inFile);
var outFile = new java.io.File(outDir+'/'+fileName);
var bis = new Packages.java.io.BufferedInputStream(new Packages.java.io.FileInputStream(inFile), 4096);
var bos = new Packages.java.io.BufferedOutputStream(new Packages.java.io.FileOutputStream(outFile), 4096);
var theChar;
@ -136,7 +141,7 @@ exports.copyFile = function(inFile, outDir, fileName) {
var toFile = exports.toFile = function(path) {
var parts = path.split(/[\\\/]/);
return parts.pop();
}
};
exports.writeFileSync = function(filename, data, encoding) {
encoding = encoding || 'utf-8';
@ -147,7 +152,7 @@ exports.writeFileSync = function(filename, data, encoding) {
encoding
)
);
try {
out.write(data);
}

View File

@ -27,9 +27,13 @@ argParser.addOption('X', 'explain', false, 'Dump all found doclet internals
argParser.addOption('q', 'query', true, 'Provide a querystring to define custom variable names/values to add to the options hash.');
argParser.addOption('u', 'tutorials', true, 'Directory in which JSDoc should search for tutorials.');
//TODO [-R, recurseonly] = a number representing the depth to recurse
//TODO [-f, filter] = a regex to filter on <-- this can be better defined in the configs?
// TODO [-R, recurseonly] = a number representing the depth to recurse
// TODO [-f, filter] = a regex to filter on <-- this can be better defined in the configs?
//Here are options specific to tests
argParser.addOption(null, 'verbose', false, 'Display verbose output for tests');
argParser.addOption(null, 'match', true, 'only run tests containing <value>', true);
argParser.addOption(null, 'coffee', false, 'load coffee-script which allows execution .coffee files');
/**
Set the options for this app.
@ -38,13 +42,13 @@ argParser.addOption('u', 'tutorials', true, 'Directory in which JSDoc should
*/
exports.parse = function(args) {
args = args || [];
if (typeof args === 'string' || args.constructor === String) {
args = (''+args).split(/\s+/g);
}
ourOptions = argParser.parse(args, defaults);
return ourOptions;
}

View File

@ -183,5 +183,5 @@ exports.attachTo = function(parser) {
}
}
}
}
};

View File

@ -25,7 +25,7 @@ exports.Parser = function() {
}
};
this._visitors = [];
}
};
require('common/util').mixin(exports.Parser.prototype, require('common/events'));
/**
@ -72,7 +72,7 @@ exports.Parser.prototype.parse = function(sourceFiles, encoding) {
}
return this._resultBuffer;
}
};
/**
* @returns {Array<Doclet>} The accumulated results of any calls to parse.

View File

@ -1,17 +1,146 @@
var isWindows = java.lang.System.getProperty("os.name").toLowerCase().contains("windows");
var fileSeparator = java.lang.System.getProperty("file.separator");
/**
* Returns everything on a path except for the last item
* e.g. if the path was 'path/to/something', the return value would be 'path/to'
*/
exports.basename = function(path) {
var parts = path.split('/');
var parts = path.split(fileSeparator);
parts.pop();
path = parts.join('/');
path = parts.join(fileSeparator);
return path;
};
/**
* Returns the last item on a path
*/
exports.filename = function(path) {
var parts = path.split(fileSeparator);
if (parts.length > 0) {
return parts.pop();
}
return null;
};
exports.existsSync = function(path) {
var file = new java.io.File(path);
if (file.isFile()) {
return true;
}
return false;
};
};
//Code below taken from node
//resolves . and .. elements in a path array with directory names there
//must be no slashes, empty elements, or device names (c:\) in the array
//(so also no leading and trailing slashes - it does not distinguish
//relative and absolute paths)
function normalizeArray(parts, allowAboveRoot) {
// if the path tries to go above the root, `up` ends up > 0
var up = 0;
for ( var i = parts.length - 1; i >= 0; i--) {
var last = parts[i];
if (last == '.') {
parts.splice(i, 1);
} else if (last === '..') {
parts.splice(i, 1);
up++;
} else if (up) {
parts.splice(i, 1);
up--;
}
}
// if the path is allowed to go above the root, restore leading ..s
if (allowAboveRoot) {
for (; up--; up) {
parts.unshift('..');
}
}
return parts;
}
if (isWindows) {
// Regex to split a windows path into three parts: [*, device, slash,
// tail] windows-only
var splitDeviceRe =
/^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?([\\\/])?([\s\S]*?)$/;
// windows version
exports.normalize = function(path) {
var result = splitDeviceRe.exec(path),
device = result[1] || '',
isUnc = device && device.charAt(1) !== ':',
isAbsolute = !!result[2] || isUnc, // UNC paths are always absolute
tail = result[3],
trailingSlash = /[\\\/]$/.test(tail);
// Normalize the tail path
tail = normalizeArray(tail.split(/[\\\/]+/).filter(function(p) {
return !!p;
}), !isAbsolute).join('\\');
if (!tail && !isAbsolute) {
tail = '.';
}
if (tail && trailingSlash) {
tail += '\\';
}
return device + (isAbsolute ? '\\' : '') + tail;
};
//windows version
exports.join = function() {
function f(p) {
return p && typeof p === 'string';
}
var paths = Array.prototype.slice.call(arguments, 0).filter(f);
var joined = paths.join('\\');
// Make sure that the joined path doesn't start with two slashes
// - it will be mistaken for an unc path by normalize() -
// unless the paths[0] also starts with two slashes
if (/^[\\\/]{2}/.test(joined) && !/^[\\\/]{2}/.test(paths[0])) {
joined = joined.slice(1);
}
return exports.normalize(joined);
};
} else {
// path.normalize(path)
// posix version
exports.normalize = function(path) {
var isAbsolute = path.charAt(0) === '/',
trailingSlash = path.slice(-1) === '/';
// Normalize the path
path = normalizeArray(path.split('/').filter(function(p) {
return !!p;
}), !isAbsolute).join('/');
if (!path && !isAbsolute) {
path = '.';
}
if (path && trailingSlash) {
path += '/';
}
return (isAbsolute ? '/' : '') + path;
};
// posix version
exports.join = function() {
var paths = Array.prototype.slice.call(arguments, 0);
return exports.normalize(paths.filter(function(p, index) {
return p && typeof p === 'string';
}).join('/'));
};
}

View File

@ -1,6 +1,46 @@
From the project root, run the following command in the terminal:
Testing JSDoc 3
===============
Running Tests
-------------
Running tests is easy. Just change your working directory to the jsdoc folder
and run the following command on Windows:
jsdoc -T
... or on a Max OSX or *nix platform:
./jsdoc -T
If you can't get the short-form commands to work, try invoking Java directly:
java -cp lib/js.jar org.mozilla.javascript.tools.shell.Main \
-modules node_modules -modules rhino_modules -modules . \
jsdoc.js -T
Writing Tests
-------------
Adding tests is pretty easy, too. You can write tests for jsdoc itself (to
make sure tags and the parser, etc. are working properly), tests for plugins, and/or
tests for templates.
JSDoc 3 uses Jasmine (https://github.com/pivotal/jasmine) as its testing framework.
Take a look at that project's wiki for documentation on writing tests in general.
### Tests for JSDoc
And take a look at the files in the ```test``` directory for many examples of
writing tests for JSDoc itself. the ```test\fixtures``` directory hold fixtures
for use in the tests and the ```test\tests``` directory holds the tests themselves.
### Tests for plugins
java -jar lib/js.jar -modules rhino_modules -modules node_modules jsdoc.js -T
TODO:

56
test/async-callback.js Normal file
View File

@ -0,0 +1,56 @@
(function() {
var withoutAsync = {};
["it", "beforeEach", "afterEach"].forEach(function(jasmineFunction) {
withoutAsync[jasmineFunction] = jasmine.Env.prototype[jasmineFunction];
return jasmine.Env.prototype[jasmineFunction] = function() {
var args = Array.prototype.slice.call(arguments, 0);
var timeout = null;
if (isLastArgumentATimeout(args)) {
timeout = args.pop();
}
if (isLastArgumentAnAsyncSpecFunction(args))
{
var specFunction = args.pop();
args.push(function() {
return asyncSpec(specFunction, this, timeout);
});
}
return withoutAsync[jasmineFunction].apply(this, args);
};
});
function isLastArgumentATimeout(args)
{
return args.length > 0 && (typeof args[args.length-1]) === "number";
}
function isLastArgumentAnAsyncSpecFunction(args)
{
return args.length > 0 && (typeof args[args.length-1]) === "function" && args[args.length-1].length > 0;
}
function asyncSpec(specFunction, spec, timeout) {
if (timeout == null){timeout = jasmine.DEFAULT_TIMEOUT_INTERVAL || 1000;}
var done = false;
spec.runs(function() {
try {
return specFunction(function(error) {
done = true;
if (error != null) {
return spec.fail(error);
}
});
} catch (e) {
done = true;
throw e;
}
});
return spec.waitsFor(function() {
if (done === true) {
return true;
}
}, "spec to complete", timeout);
};
}).call(this);

128
test/jasmine-jsdoc.js Normal file
View File

@ -0,0 +1,128 @@
/*First load envjs to give us a browser environment for jasmine.
*Jasmine wants things like set/clearInterval,set/clearTimeout.
*Then load jasmine itself
*/
load('test/lib/env.rhino.js');
load('test/lib/jasmine.js');
load('test/async-callback.js');
var jasmineNode = require('./reporter').jasmineNode,
util = require('common/util');
jasmine.loadHelpersInFolder = function(folder, matcher) {
var helpers = [], helperCollection = require('./spec-collection');
helperCollection.load(folder, matcher, true);
helpers = helperCollection.getSpecs();
for ( var i = 0, len = helpers.length; i < len; ++i) {
var file = helpers[i].path();
var helper = require(file.replace(/\.*$/, ""));
for (var key in helper) {
this[key] = helper[key];
}
}
};
function removeJasmineFrames(text) {
var lines = [];
text.split(/\n/).forEach(function(line) {
if (line.indexOf(filename) == -1) {
lines.push(line);
}
});
return lines.join('\n');
}
var reporter = null;
jasmine.initialize = function(done, verbose) {
var jasmineEnv = jasmine.getEnv();
if (reporter !== null) {
//If we've run before, we need to reset the runner
jasmineEnv.currentRunner_ = new jasmine.Runner(jasmineEnv);
//And clear the reporter
jasmineEnv.reporter.subReporters_.splice(jasmineEnv.reporter.subReporters_.indexOf(reporter));
}
reporter = new (verbose ? jasmineNode.TerminalVerboseReporter : jasmineNode.TerminalReporter)({
print : util.print,
color : true,
onComplete : done,
stackFilter : removeJasmineFrames
});
jasmineEnv.addReporter(reporter);
//updateInterval is set to 0 because there were not-fully-understood
//issues with asynchronous behavior in jasmine otherwise.
jasmineEnv.updateInterval = 0;
return jasmineEnv;
};
/**
* Execute the specs in the specified folder. Helpers in each folder will be
* added to the environment. Helpers in parent directories will be available to child
* directories.
* @param {string} folder the folder in which the specs are to be found
* @param {function?} done callback function to execute when finished
* @param {boolean} verbose whether or not output verbose results
* @param {RegExp} matcher a regular expression to filter specs by. Only matching specs will run
*/
jasmine.executeSpecsInFolder = function(folder, done, verbose, matcher) {
var fileMatcher = matcher || new RegExp(".(js)$", "i"),
specs = require('./spec-collection'),
jasmineEnv = jasmine.initialize(done, verbose);
//Load the specs
specs.load(folder, fileMatcher, true);
//Add the specs to the context
var specsList = specs.getSpecs();
for ( var i = 0, len = specsList.length; i < len; ++i) {
var filename = specsList[i];
require(filename.path().replace(/\.\w+$/, ""));
}
//Run Jasmine
jasmineEnv.execute();
};
function now() {
return new Date().getTime();
}
jasmine.asyncSpecWait = function() {
var wait = this.asyncSpecWait;
wait.start = now();
wait.done = false;
(function innerWait() {
waits(10);
runs(function() {
if (wait.start + wait.timeout < now()) {
expect('timeout waiting for spec').toBeNull();
} else if (wait.done) {
wait.done = false;
} else {
innerWait();
}
});
})();
};
jasmine.asyncSpecWait.timeout = 4 * 1000;
jasmine.asyncSpecDone = function() {
jasmine.asyncSpecWait.done = true;
};
for ( var key in jasmine) {
exports[key] = jasmine[key];
}
exports.spyOn = spyOn;
exports.it = it;
exports.xit = xit;
exports.expect = expect;
exports.runs = runs;
exports.waitsFor = waitsFor;
exports.beforeEach = beforeEach;
exports.afterEach = afterEach;
exports.describe = describe;
exports.xdescribe = xdescribe;

14006
test/lib/env.rhino.js Normal file

File diff suppressed because one or more lines are too long

2547
test/lib/jasmine.js Normal file

File diff suppressed because it is too large Load Diff

299
test/reporter.js Normal file
View File

@ -0,0 +1,299 @@
(function() {
//
// Imports
//
if (!jasmineNode) {
var jasmineNode = {};
}
//
// Helpers
//
function noop() {
}
jasmineNode.ANSIColors = {
pass : function() {
return '\033[32m';
}, // Green
fail : function() {
return '\033[31m';
}, // Red
neutral : function() {
return '\033[0m';
} // Normal
};
jasmineNode.NoColors = {
pass : function() {
return '';
},
fail : function() {
return '';
},
neutral : function() {
return '';
}
};
jasmineNode.TerminalReporter = function(config) {
this.print_ = config.print || print;
this.color_ = config.color ? jasmineNode.ANSIColors : jasmineNode.NoColors;
this.started_ = false;
this.finished_ = false;
this.callback_ = config.onComplete || false;
this.suites_ = [];
this.specResults_ = {};
this.failures_ = {};
this.failures_.length = 0;
};
jasmineNode.TerminalReporter.prototype = {
reportRunnerStarting : function(runner) {
this.started_ = true;
this.startedAt = new Date();
var suites = runner.topLevelSuites();
for ( var i = 0; i < suites.length; i++) {
var suite = suites[i];
this.suites_.push(this.summarize_(suite));
}
},
summarize_ : function(suiteOrSpec) {
var isSuite = suiteOrSpec instanceof jasmine.Suite;
// We could use a separate object for suite and spec
var summary = {
id : suiteOrSpec.id,
name : suiteOrSpec.description,
type : isSuite ? 'suite' : 'spec',
suiteNestingLevel : 0,
children : []
};
if (isSuite) {
var calculateNestingLevel = function(examinedSuite) {
var nestingLevel = 0;
while (examinedSuite.parentSuite !== null) {
nestingLevel += 1;
examinedSuite = examinedSuite.parentSuite;
}
return nestingLevel;
};
summary.suiteNestingLevel = calculateNestingLevel(suiteOrSpec);
var children = suiteOrSpec.children();
for ( var i = 0; i < children.length; i++) {
summary.children.push(this.summarize_(children[i]));
}
}
return summary;
},
// This is heavily influenced by Jasmine's Html/Trivial Reporter
reportRunnerResults : function(runner) {
this.reportFailures_();
var results = runner.results();
var resultColor = (results.failedCount > 0) ? this.color_.fail() : this.color_.pass();
var specs = runner.specs();
var specCount = specs.length;
var message = "\n\nFinished in "
+ ((new Date().getTime() - this.startedAt.getTime()) / 1000)
+ " seconds";
this.printLine_(message);
// This is what jasmine-html.js has
// message = "" + specCount + " spec" + ( specCount === 1 ? "" : "s") + ", " + results.failedCount + " failure" + ((results.failedCount === 1) ? "" : "s");
this.printLine_(this.stringWithColor_(this.printRunnerResults_(runner), resultColor));
this.finished_ = true;
if (this.callback_) {
this.callback_(runner);
}
},
reportFailures_ : function() {
if (this.failures_.length === 0) {
return;
}
var indent = ' ', failure;
this.printLine_('\n');
this.print_('Failures:');
for ( var suite in this.failures_) {
if (this.failures_.hasOwnProperty(suite) && suite !== "length") {
this.printLine_('\n');
this.printLine_(suite);
failures = this.failures_[suite];
for ( var i = 0; i < failures.length; i++) {
failure = failures[i];
this.printLine_('\n');
this.printLine_(indent + (i + 1) + ') ' + failure.spec);
this.printLine_(indent + 'Message:');
this.printLine_(indent + indent + this.stringWithColor_(failure.message, this.color_.fail()));
this.printLine_(indent + 'Stacktrace:');
this.print_(indent + indent + failure.stackTrace);
}
}
}
},
reportSuiteResults : function(suite) {
// Not used in this context
},
reportSpecResults : function(spec) {
var result = spec.results();
var msg = '';
if (result.passed()) {
msg = this.stringWithColor_('.', this.color_.pass());
// } else if (result.skipped) { TODO: Research why "result.skipped" returns false when "xit" is called on a spec?
// msg = (colors) ? (ansi.yellow + '*' + ansi.none) : '*';
} else {
msg = this.stringWithColor_('F', this.color_.fail());
this.addFailureToFailures_(spec);
}
this.spec_results += msg;
this.print_(msg);
},
addFailureToFailures_ : function(spec) {
var result = spec.results();
var failureItem = null;
var suite = spec.suite.getFullName();
var failures = null;
var items_length = result.items_.length;
for ( var i = 0; i < items_length; i++) {
if (result.items_[i].passed_ === false) {
failureItem = result.items_[i];
var failure = {
spec : spec.description,
message : failureItem.message,
stackTrace : failureItem.trace.stack
};
failures = this.failures_[suite];
if (!failures) {
this.failures_[suite] = [];
}
this.failures_[suite].push(failure);
this.failures_.length++;
}
}
},
printRunnerResults_ : function(runner) {
var results = runner.results();
var specs = runner.specs();
var msg = '';
msg += specs.length + ' test' + ((specs.length === 1) ? '' : 's') + ', ';
msg += results.totalCount + ' assertion' + ((results.totalCount === 1) ? '' : 's') + ', ';
msg += results.failedCount + ' failure' + ((results.failedCount === 1) ? '' : 's') + '\n';
return msg;
},
// Helper Methods //
stringWithColor_ : function(stringValue, color) {
return (color || this.color_.neutral()) + stringValue + this.color_.neutral();
},
printLine_ : function(stringValue) {
this.print_(stringValue);
this.print_('\n');
}
};
// ***************************************************************
// TerminalVerboseReporter uses the TerminalReporter's constructor
// ***************************************************************
jasmineNode.TerminalVerboseReporter = function(config) {
jasmineNode.TerminalReporter.call(this, config);
// The extra field in this object
this.indent_ = 0;
};
jasmineNode.TerminalVerboseReporter.prototype = {
reportSpecResults : function(spec) {
if (spec.results().failedCount > 0) {
this.addFailureToFailures_(spec);
}
this.specResults_[spec.id] = {
messages : spec.results().getItems(),
result : spec.results().failedCount > 0 ? 'failed' : 'passed'
};
},
reportRunnerResults : function(runner) {
var messages = new Array();
this.buildMessagesFromResults_(messages, this.suites_);
var messages_length = messages.length;
for ( var i = 0; i < messages_length - 1; i++) {
this.printLine_(messages[i]);
}
this.print_(messages[messages_length - 1]);
// Call the parent object's method
jasmineNode.TerminalReporter.prototype.reportRunnerResults.call(this, runner);
},
buildMessagesFromResults_ : function(messages, results) {
var element, specResult, specIndentSpaces, msg = '';
var results_length = results.length;
for ( var i = 0; i < results_length; i++) {
element = results[i];
if (element.type === 'spec') {
specResult = this.specResults_[element.id.toString()];
specIndentSpaces = this.indent_ + 2;
if (specResult.result === 'passed') {
msg = this.stringWithColor_(this.indentMessage_(element.name, specIndentSpaces), this.color_.pass());
} else {
msg = this.stringWithColor_(this.indentMessage_(element.name, specIndentSpaces), this.color_.fail());
}
messages.push(msg);
} else {
this.indent_ = element.suiteNestingLevel * 2;
messages.push('');
messages.push(this.indentMessage_(element.name,this.indent_));
}
this.buildMessagesFromResults_(messages, element.children);
}
},
indentMessage_ : function(message, indentCount) {
var _indent = '';
for ( var i = 0; i < indentCount; i++) {
_indent += ' ';
}
return (_indent + message);
}
};
// Inherit from TerminalReporter
jasmineNode.TerminalVerboseReporter.prototype.__proto__ = jasmineNode.TerminalReporter.prototype;
//
// Exports
//
exports.jasmineNode = jasmineNode;
})();

View File

@ -1,38 +1,45 @@
var assert = require('common/assert');
/*
* Tests Steps:
* 1. Get Jasmine
* 2. Get the test options
* 3. Get the list of directories to run tests from
* 4. Run Jasmine on each directory
*/
var jasmine = require('test/jasmine-jsdoc');
var extensions = "js";
var match = ".";
var verbose = env.opts.verbose || false;
var coffee = env.opts.coffee || false;
var matches = env.opts.match || false;
if (coffee) {
extensions = "js|coffee";
}
if (matches) {
if (matches instanceof Array) {
match = matches.join("|");
} else {
match = matches;
}
}
var helperCollection = require('test/spec-collection');
var specFolders = ['test/specs', 'plugins/test/specs'];
var failedCount = 0;
var index = 0;
for (var key in jasmine) {
this[key] = jasmine[key];
}
// --------------OLD------------------
var passCount = 0,
failCount = 0,
errorLog = [],
currentTestFile = '';
function test(description, f) {
try {
f();
passCount++;
}
catch(e) {
errorLog.push(description + (currentTestFile? ' ['+currentTestFile+']':'') + '\n' + (e.message||'') + '\n - Expected: ' + e.expected + '\n - Actual: ' + e.actual);
failCount++;
}
}
function testFile(filepath) {
currentTestFile = filepath;
include(filepath);
currentTestFile = '';
}
function report() {
console.log('\033[032mPASSED: ' + passCount + ' test' + (passCount == 1? '' : 's') + '.\033[0m');
if (failCount) {
console.log('\033[031mFAILED: '+ failCount + ' test' + (passCount == 1? '' : 's') + '.\033[0m');
for (var i = 0, leni = errorLog.length; i < leni; i++) {
console.log(' ' + (i+1) + '. ' + (i+1 < 10? ' ' : '') + (errorLog[i]||'') + '\n');
}
}
}
// helpers
//helpers
var testhelpers = {
getDocSetFromFile: function(filename) {
var sourceCode = readFile(__dirname + '/' + filename),
@ -69,104 +76,141 @@ var testhelpers = {
}
};
testFile('test/t/common/util.js');
testFile('test/t/common/dumper.js');
testFile('test/t/common/events.js');
testFile('test/t/common/query.js');
testFile('test/t/common/scanner.js');
//--------------END OLD------------------
testFile('test/t/jsdoc/opts/parser.js');
testFile('test/t/jsdoc/src/parser.js');
testFile('test/t/jsdoc/src/handlers.js');
testFile('test/t/jsdoc/name.js');
testFile('test/t/jsdoc/util/templateHelper.js');
var onComplete = function(runner, log) {
if (runner.results().failedCount != 0) {
failedCount++;
}
index++;
runNextFolder();
};
testFile('test/t/cases/file.js');
var specFolder = null;
testFile('test/t/cases/virtual.js');
var runNextFolder = function() {
if (index < specFolders.length) {
jasmine.loadHelpersInFolder(specFolders[index], new RegExp("helpers\\.(" + extensions + ")$", 'i'));
testFile('test/t/cases/objectlit.js');
testFile('test/t/cases/objectlit2.js');
var regExpSpec = new RegExp("(" + match + ")\\.(" + extensions + ")$", 'i');
jasmine.executeSpecsInFolder(specFolders[index], onComplete, verbose, regExpSpec);
}
};
testFile('test/t/cases/this.js');
testFile('test/t/cases/this2.js');
testFile('test/t/cases/this3.js');
runNextFolder();
//process.exit(failedCount);
testFile('test/t/cases/this-and-objectlit.js');
//--------------OLD------------------
function test(description, f) {
try {
f();
passCount++;
}
catch(e) {
errorLog.push(description + (currentTestFile? ' ['+currentTestFile+']':'') + '\n' + (e.message||'') + '\n - Expected: ' + e.expected + '\n - Actual: ' + e.actual);
failCount++;
}
}
testFile('test/t/cases/var.js');
function testFile(filepath) {
currentTestFile = filepath;
include(filepath);
currentTestFile = '';
}
testFile('test/t/cases/inner.js');
testFile('test/t/cases/innerscope.js');
testFile('test/t/cases/innerscope2.js');
function report() {
console.log('\033[032mPASSED: ' + passCount + ' test' + (passCount == 1? '' : 's') + '.\033[0m');
if (failCount) {
console.log('\033[031mFAILED: '+ failCount + ' test' + (passCount == 1? '' : 's') + '.\033[0m');
for (var i = 0, leni = errorLog.length; i < leni; i++) {
console.log(' ' + (i+1) + '. ' + (i+1 < 10? ' ' : '') + (errorLog[i]||'') + '\n');
}
}
}
testFile('test/t/cases/modules/data/mod-1.js');
testFile('test/t/cases/modules/data/mod-2.js');
testFile('test/specs/tags/file.js');
testFile('test/t/cases/abstracttag.js');
testFile('test/t/cases/accesstag.js');
testFile('test/t/cases/alias.js');
testFile('test/t/cases/alias2.js');
testFile('test/t/cases/alias3.js');
testFile('test/t/cases/aliasglobal.js');
testFile('test/t/cases/aliasresolve.js');
testFile('test/t/cases/aliasresolve2.js');
testFile('test/t/cases/also.js');
testFile('test/t/cases/augmentstag.js');
testFile('test/t/cases/authortag.js');
testFile('test/t/cases/borrowstag.js');
testFile('test/t/cases/borrowstag2.js');
testFile('test/t/cases/classtag.js');
testFile('test/t/cases/constructstag.js');
testFile('test/t/cases/constructstag2.js');
testFile('test/t/cases/constructstag3.js');
testFile('test/t/cases/constructstag4.js');
testFile('test/t/cases/constructstag5.js');
testFile('test/t/cases/constructortag.js');
testFile('test/t/cases/constructorproperty.js');
testFile('test/t/cases/copyrighttag.js');
testFile('test/t/cases/defaulttag.js');
testFile('test/t/cases/deprecatedtag.js');
testFile('test/t/cases/enumtag.js');
testFile('test/t/cases/eventfirestag.js');
testFile('test/t/cases/exports.js');
testFile('test/t/cases/exportstag.js');
testFile('test/t/cases/exportstag2.js');
testFile('test/t/cases/exportstag3.js');
testFile('test/t/cases/exportstag4.js');
testFile('test/t/cases/exceptiontag.js');
testFile('test/t/cases/globaltag.js');
testFile('test/t/cases/ignoretag.js');
testFile('test/t/cases/inlinecomment.js');
testFile('test/t/cases/lends.js');
testFile('test/t/cases/lends2.js');
testFile('test/t/cases/lendsglobal.js');
testFile('test/t/cases/moduleinner.js');
testFile('test/t/cases/memberoftag.js');
testFile('test/t/cases/memberoftag2.js');
testFile('test/t/cases/memberoftag3.js');
testFile('test/t/cases/memberoftag4.js');
testFile('test/t/cases/memberoftagforced.js');
testFile('test/t/cases/moduletag.js');
testFile('test/t/cases/moduletag2.js');
testFile('test/t/cases/namedFuncStatement.js');
testFile('test/t/cases/paramtag.js');
testFile('test/t/cases/privatetag.js');
testFile('test/t/cases/quotename.js');
testFile('test/t/cases/quotename2.js');
testFile('test/t/cases/readonlytag.js');
testFile('test/t/cases/requirestag.js');
testFile('test/t/cases/returnstag.js');
testFile('test/t/cases/seetag.js');
testFile('test/t/cases/sincetag.js');
testFile('test/t/cases/starbangstar.js');
testFile('test/t/cases/thistag.js');
testFile('test/t/cases/typekind.js');
testFile('test/t/cases/typetag.js');
testFile('test/t/cases/typedeftag.js');
testFile('test/t/cases/variations.js');
testFile('test/t/cases/variations3.js');
testFile('test/t/cases/versiontag.js');
testFile('test/specs/documentation/virtual.js');
testFile('test/specs/documentation/objectlit.js');
testFile('test/specs/documentation/objectlit2.js');
testFile('test/specs/documentation/this.js');
testFile('test/specs/documentation/this2.js');
testFile('test/specs/documentation/this3.js');
testFile('test/specs/documentation/this-and-objectlit.js');
testFile('test/specs/documentation/var.js');
testFile('test/specs/documentation/inner.js');
testFile('test/specs/documentation/innerscope.js');
testFile('test/specs/documentation/innerscope2.js');
testFile('test/specs/tags/abstracttag.js');
testFile('test/specs/tags/accesstag.js');
testFile('test/specs/tags/alias.js');
testFile('test/specs/tags/alias2.js');
testFile('test/specs/tags/alias3.js');
testFile('test/specs/tags/aliasglobal.js');
testFile('test/specs/tags/aliasresolve.js');
testFile('test/specs/tags/aliasresolve2.js');
testFile('test/specs/documentation/also.js');
testFile('test/specs/tags/augmentstag.js');
testFile('test/specs/tags/authortag.js');
testFile('test/specs/tags/borrowstag.js');
testFile('test/specs/tags/borrowstag2.js');
testFile('test/specs/tags/classtag.js');
testFile('test/specs/tags/constructstag.js');
testFile('test/specs/tags/constructstag2.js');
testFile('test/specs/tags/constructstag3.js');
testFile('test/specs/tags/constructstag4.js');
testFile('test/specs/tags/constructstag5.js');
testFile('test/specs/tags/constructortag.js');
testFile('test/specs/documentation/constructorproperty.js');
testFile('test/specs/tags/copyrighttag.js');
testFile('test/specs/tags/defaulttag.js');
testFile('test/specs/tags/deprecatedtag.js');
testFile('test/specs/tags/enumtag.js');
testFile('test/specs/tags/eventfirestag.js');
testFile('test/specs/documentation/exports.js');
testFile('test/specs/tags/exportstag.js');
testFile('test/specs/tags/exportstag2.js');
testFile('test/specs/tags/exportstag3.js');
testFile('test/specs/tags/exportstag4.js');
testFile('test/specs/tags/exceptiontag.js');
testFile('test/specs/tags/globaltag.js');
testFile('test/specs/tags/ignoretag.js');
testFile('test/specs/documentation/inlinecomment.js');
testFile('test/specs/tags/lends.js');
testFile('test/specs/tags/lends2.js');
testFile('test/specs/tags/lendsglobal.js');
testFile('test/specs/documentation/moduleinner.js');
testFile('test/specs/tags/memberoftag.js');
testFile('test/specs/tags/memberoftag2.js');
testFile('test/specs/tags/memberoftag3.js');
testFile('test/specs/tags/memberoftag4.js');
testFile('test/specs/tags/memberoftagforced.js');
testFile('test/specs/tags/moduletag.js');
testFile('test/specs/tags/moduletag2.js');
testFile('test/specs/documentation/namedFuncStatement.js');
testFile('test/specs/tags/paramtag.js');
testFile('test/specs/tags/privatetag.js');
testFile('test/specs/documentation/quotename.js');
testFile('test/specs/documentation/quotename2.js');
testFile('test/specs/tags/readonlytag.js');
testFile('test/specs/tags/requirestag.js');
testFile('test/specs/tags/returnstag.js');
testFile('test/specs/tags/seetag.js');
testFile('test/specs/tags/sincetag.js');
testFile('test/specs/documentation/starbangstar.js');
testFile('test/specs/tags/thistag.js');
testFile('test/specs/tags/typekind.js');
testFile('test/specs/tags/typetag.js');
testFile('test/specs/tags/typedeftag.js');
testFile('test/specs/documentation/variations.js');
testFile('test/specs/tags/variations3.js');
testFile('test/specs/tags/versiontag.js');
var os = java.lang.System.getProperty('os.name'),
@ -185,4 +229,5 @@ function green(str) {
}
report();
report();
//--------------END OLD------------------

51
test/spec-collection.js Normal file
View File

@ -0,0 +1,51 @@
var wrench = require('wrench/wrench');
var path = require('path');
var fs = require('fs');
var specs = [];
var createSpecObj = function(path, root) {
return {
path : function() {
return path;
},
relativePath : function() {
return path.replace(root, '').replace(/^[\/\\]/, '').replace(/\\/g, '/');
},
directory : function() {
return path.replace(/[\/\\][\s\w\.-]*$/, "").replace(/\\/g, '/');
},
relativeDirectory : function() {
return relativePath().replace(/[\/\\][\s\w\.-]*$/, "").replace(/\\/g, '/');
},
filename : function() {
return path.replace(/^.*[\\\/]/, '');
}
};
};
var clearSpecs = exports.clearSpecs = function() {
specs.splice(0, specs.length);
};
exports.load = function(loadpath, matcher, clear) {
if(clear === true) {
clearSpecs();
}
var wannaBeSpecs = wrench.readdirSyncRecursive(loadpath);
for (var i = 0; i < wannaBeSpecs.length; i++) {
var file = path.join(__dirname, loadpath, wannaBeSpecs[i]);
try {
if (fs.statSync(file).isFile()) {
if (!/.*node_modules.*/.test(file) && matcher.test(path.filename(file))) {
specs.push(createSpecObj(file));
}
}
} catch(e) {
// nothing to do here
}
}
};
exports.getSpecs = function() {
return specs;
};

View File

@ -0,0 +1,36 @@
describe("common/events", function() {
var common = {events: require('common/events')};
it('should exist', function() {
expect(common.events).toBeDefined();
expect(typeof common.events).toEqual("object");
});
it('should export a "on" function.', function() {
expect(common.events.on).toBeDefined();
expect(typeof common.events.on).toEqual("function");
});
it('should export a "fire" function.', function() {
expect(common.events.fire).toBeDefined();
expect(typeof common.events.fire).toEqual("function");
});
it('should export a "removeListener" function.', function() {
expect(common.events.removeListener).toBeDefined();
expect(typeof common.events.removeListener).toEqual("function");
});
it('The "on" function attaches a handler to an object that can be fired.', function() {
var target = {},
result = false;
target.on = common.events.on;
target.fire = common.events.fire;
target.on('test', function() { result = true; });
target.fire('test');
expect(result).toEqual(true);
});
});

View File

@ -0,0 +1,18 @@
describe("common/query", function() {
var common = {query: require('common/query')};
it('should exist', function() {
expect(common.query).toBeDefined();
expect(typeof common.query).toEqual("object");
});
it('should export a "toObject" function.', function() {
expect(common.query.toObject).toBeDefined();
expect(typeof common.query.toObject).toEqual("function");
});
it('The "toObject" function dumps an object from a query string.', function() {
expect(common.query.toObject('name=Michael+Mathews')).toEqual({name: 'Michael Mathews'});
expect(common.query.toObject('name=Michael+Mathews&city=London')).toEqual({name: 'Michael Mathews', city: 'London'});
});
});

77
test/specs/common/util.js Normal file
View File

@ -0,0 +1,77 @@
describe("common/util", function() {
var common = {util: require('common/util')};
it('should exist', function() {
expect(common.util).toBeDefined();
expect(typeof common.util).toEqual("object");
});
it('should export a "inherits" function.', function() {
expect(common.util.inherits).toBeDefined();
expect(typeof common.util.inherits).toEqual("function");
});
it('should export a "mixin" function.', function() {
expect(common.util.mixin).toBeDefined();
expect(typeof common.util.mixin).toEqual("function");
});
describe("common/util.mixin", function() {
it('should take a target object and return it.', function() {
var target = {a:1},
returned;
returned = common.util.mixin(target); // mixing nothing in
expect(returned).toEqual(target);
});
it('it should mix a source object into the target.', function() {
var target = {a: 1, b: 2},
source = {c: 3};
common.util.mixin(target, source); // modify the target object
expect(target).toEqual({a: 1, b: 2, c: 3});
});
describe("overwriting properties in the target", function() {
var target = {a: 1, b: 2},
source = {b: 3, c: 4};
common.util.mixin(target, source);
it("should leave properties in the target with unique keys alone", function() {
expect(target.a).toEqual(1);
});
it ('should overwrite existing properties in the target with same-named keys', function() {
expect(target.b).toEqual(source.b);
});
it ('should add properties in the source with unique keys to the target', function() {
expect(target.c).toEqual(source.c);
});
});
describe("mixing several objects into the target", function() {
var target = {},
source1 = {a: 1, b: 2},
source2 = {b: 7, c: 4},
source3 = {b: 3, d: 5},
returned;
returned = common.util.mixin(target, source1, source2, source3); // use a dummy target and the return value to avoid modifying the real target (source1)
it ('should not modify the source objects being mixed in', function() {
expect(source1).toEqual({a: 1, b: 2});
});
it ('should return an object with the properties of all the sources', function() {
expect(returned).toEqual({a: 1, b: 3, c: 4, d: 5});
});
});
});
});

View File

@ -3,9 +3,9 @@
name = docSet.getByLongname('Asset#name').filter(function($) {
return ! $.undocumented;
});
test('When a symbol has two doclets adjacent to each other both doclets apply to the symbol.', function() {
assert.equal(name.length, 2, 'myObject');
});
})();

View File

@ -0,0 +1,22 @@
describe("module names", function() {
var parser = require('jsdoc/src/parser'),
srcParser = null, doclets;
beforeEach(function() {
env.opts._ = [__dirname + '/test/cases/modules/'];
srcParser = new parser.Parser();
require('jsdoc/src/handlers').attachTo(srcParser);
});
it("should create a name from the file path when no documented module name exists", function() {
doclets = srcParser.parse(__dirname + '/test/cases/modules/data/mod-1.js');
assert.ok(doclets.length > 1);
assert.equal(doclets[0].longname, 'module:data/mod-1');
});
it("should use the documented module name if available", function() {
doclets = srcParser.parse(__dirname + '/test/cases/modules/data/mod-2.js');
assert.ok(doclets.length > 1);
assert.equal(doclets[0].longname, 'module:my/module/name');
});
});

34
test/specs/helpers.js Normal file
View File

@ -0,0 +1,34 @@
exports.getDocSetFromFile = function(filename) {
var sourceCode = readFile(__dirname + '/' + filename),
testParser,
doclets;
testParser = new (require('jsdoc/src/parser')).Parser();
require('jsdoc/src/handlers').attachTo(testParser);
doclets = testParser.parse('javascript:' + sourceCode);
testhelpers.indexAll(doclets);
require('jsdoc/augment').addInherited(doclets);
// test assume borrows have not yet been resolved
// require('jsdoc/borrow').resolveBorrows(doclets);
return {
doclets: doclets,
getByLongname: function(longname) {
return doclets.filter(function(doclet) {
return (doclet.longname || doclet.name) === longname;
});
}
};
};
exports.indexAll = function(docs) {
var index = {};
docs.forEach(function(doc) {
if (!index.hasOwnProperty(doc.longname)){index[doc.longname] = [];}
index[doc.longname].push(doc);
});
docs.index = index;
};

View File

@ -0,0 +1,3 @@
describe("jsdoc/augment", function() {
//TODO
});

View File

@ -0,0 +1,3 @@
describe("jsdoc/borrow", function() {
//TODO
});

View File

@ -0,0 +1,3 @@
describe("jsdoc/doclet", function() {
//TODO
});

157
test/specs/jsdoc/name.js Normal file
View File

@ -0,0 +1,157 @@
describe("jsdoc/name", function() {
var jsdoc = {name: require('jsdoc/name') };
it("should exist", function() {
expect(jsdoc.name).toBeDefined();
expect(typeof jsdoc.name).toEqual("object");
});
it("should export an 'resolve' function", function() {
expect(jsdoc.name.resolve).toBeDefined();
expect(typeof jsdoc.name.resolve).toEqual("function");
});
it("should export an 'applyNamespace' function", function() {
expect(jsdoc.name.applyNamespace).toBeDefined();
expect(typeof jsdoc.name.applyNamespace).toEqual("function");
});
it("should export an 'shorten' function", function() {
expect(jsdoc.name.shorten).toBeDefined();
expect(typeof jsdoc.name.shorten).toEqual("function");
});
it("should export an 'splitName' function", function() {
expect(jsdoc.name.splitName).toBeDefined();
expect(typeof jsdoc.name.splitName).toEqual("function");
});
describe ("shorten", function() {
it('should break up a longname into the correct memberof, name and scope parts', function() {
var startName = 'lib.Panel#open',
parts = jsdoc.name.shorten(startName);
expect(parts.name).toEqual('open');
expect(parts.memberof).toEqual('lib.Panel');
expect(parts.scope).toEqual('#');
});
it('should work on static names', function() {
var startName = 'elements.selected.getVisible',
parts = jsdoc.name.shorten(startName);
expect(parts.name).toEqual('getVisible');
expect(parts.memberof).toEqual('elements.selected');
expect(parts.scope).toEqual('.');
});
it('should work on protoyped names', function() {
var startName = 'Validator.prototype.$element',
parts = jsdoc.name.shorten(startName);
expect(parts.name).toEqual('$element');
expect(parts.memberof).toEqual('Validator');
expect(parts.scope).toEqual('#');
});
it('should work on inner names.', function() {
var startName = 'Button~_onclick',
parts = jsdoc.name.shorten(startName);
expect(parts.name).toEqual('_onclick');
expect(parts.memberof).toEqual('Button');
expect(parts.scope).toEqual('~');
});
it('should work on global names.', function() {
var startName = 'close',
parts = jsdoc.name.shorten(startName);
expect(parts.name).toEqual('close');
//'The memberof should be an empty string for global symbols.'
expect(parts.memberof).toEqual('');
//'The scope should be an empty string for global symbols.'
expect(parts.scope).toEqual('');
});
it('should work on bracketed stringy names', function() {
var startName = 'channels["#ops"]#open',
parts = jsdoc.name.shorten(startName);
expect(parts.name).toEqual('open');
//'Bracketed stringy names should appear as quoted strings.'
expect(parts.memberof).toEqual('channels."#ops"');
expect(parts.scope).toEqual('#');
startName = 'channels["#bots"]["log.max"]',
parts = jsdoc.name.shorten(startName);
expect(parts.name).toEqual('"log.max"');
expect(parts.memberof).toEqual('channels."#bots"');
expect(parts.scope).toEqual('.');
});
it('should work on fully stringy names, like "foo.bar"', function() {
var startName = '"foo.bar"',
parts = jsdoc.name.shorten(startName);
//'The name should be the full quoted string.'
expect(parts.name).toEqual('"foo.bar"');
//'The longname should be the full quoted string.'
expect(parts.longname).toEqual('"foo.bar"');
//'There should be no memberof, as it is global.'
expect(parts.memberof).toEqual('');
//'The scope should be as global.'
expect(parts.scope).toEqual('');
});
it('should find the variation', function() {
var startName = 'anim.fadein(2)',
parts = jsdoc.name.shorten(startName);
expect(parts.variation).toEqual('2');
expect(parts.name).toEqual('fadein');
expect(parts.longname).toEqual('anim.fadein(2)');
});
});
describe("applyNamespace", function() {
it('should insert the namespace only before the name part of the longname', function() {
var startName = 'lib.Panel#open',
endName = jsdoc.name.applyNamespace(startName, 'event');
expect(endName, 'lib.Panel#event:open');
});
it(" should insert the namespace before a global name", function() {
var startName = 'maths/bigint',
endName = jsdoc.name.applyNamespace(startName, 'module');
expect(endName, 'module:maths/bigint');
});
it('should treat quoted parts of the name as atomic and insert namespace before a quoted shortname', function() {
var startName = 'foo."*dont\'t.look~in#here!"',
endName = jsdoc.name.applyNamespace(startName, 'event');
expect(endName, 'foo.event:"*dont\'t.look~in#here!"');
});
it('should not add another namespace if one already exists.', function() {
var startName = 'lib.Panel#event:open',
endName = jsdoc.name.applyNamespace(startName, 'event');
expect(endName, 'lib.Panel#event:open');
});
});
describe("splitName", function() {
it('should find the name and description.', function() {
var startName = 'ns.Page#"last \\"sentence\\"".words~sort(2) - This is a description. ',
parts = jsdoc.name.splitName(startName);
expect(parts.name, 'ns.Page#"last \\"sentence\\"".words~sort(2)');
expect(parts.description, 'This is a description.');
});
});
});

View File

@ -0,0 +1,216 @@
describe("jsdoc/opts/parser", function() {
var opts = require('jsdoc/opts/parser');
it("should exist", function() {
expect(opts).toBeDefined();
expect(typeof opts).toEqual("object");
});
it("should export a 'parse' function", function() {
expect(opts.parse).toBeDefined();
expect(typeof opts.parse).toEqual("function");
});
it("should export a 'help' function", function() {
expect(opts.help).toBeDefined();
expect(typeof opts.help).toEqual("function");
});
it("should export a 'get' function", function() {
expect(opts.get).toBeDefined();
expect(typeof opts.get).toEqual("function");
});
describe("parse", function() {
it("should accept a '-t' option and return an object with a 'template' property", function() {
opts.parse(['-t', 'mytemplate']);
var r = opts.get();
expect(r.template).toEqual('mytemplate');
});
it("should accept a '--template' option and return an object with a 'template' property", function() {
opts.parse(['--template', 'mytemplate']);
var r = opts.get();
expect(r.template).toEqual('mytemplate');
});
it("should accept a '-c' option and return an object with a 'configure' property", function() {
opts.parse(['-c', 'myconf.json']);
var r = opts.get();
expect(r.configure).toEqual('myconf.json');
});
it("should accept a '--configure' option and return an object with a 'configure' property", function() {
opts.parse(['--configure', 'myconf.json']);
var r = opts.get();
expect(r.configure).toEqual('myconf.json');
});
it("should accept a '-e' option and return an object with a 'encoding' property", function() {
opts.parse(['-e', 'ascii']);
var r = opts.get();
expect(r.encoding).toEqual('ascii');
});
it("should accept a '--encoding' option and return an object with a 'encoding' property", function() {
opts.parse(['--encoding', 'ascii']);
var r = opts.get();
expect(r.encoding).toEqual('ascii');
});
it("should accept a '-T' option and return an object with a 'test' property", function() {
opts.parse(['-T']);
var r = opts.get();
expect(r.test).toEqual(true);
});
it("should accept a '--test' option and return an object with a 'test' property", function() {
opts.parse(['--test']);
var r = opts.get();
expect(r.test).toEqual(true);
});
it("should accept a '-d' option and return an object with a 'destination' property", function() {
opts.parse(['-d', 'mydestination']);
var r = opts.get();
expect(r.destination).toEqual('mydestination');
});
it("should accept a '--destination' option and return an object with a 'destination' property", function() {
opts.parse(['--destination', 'mydestination']);
var r = opts.get();
expect(r.destination).toEqual('mydestination');
});
it("should accept a '-p' option and return an object with a 'private' property", function() {
opts.parse(['-p']);
var r = opts.get();
expect(r['private']).toEqual(true);
});
it("should accept a '--private' option and return an object with a 'private' property", function() {
opts.parse(['--private']);
var r = opts.get();
expect(r['private']).toEqual(true);
});
it("should accept a '-r' option and return an object with a 'recurse' property", function() {
opts.parse(['-r']);
var r = opts.get();
expect(r.recurse).toEqual(true);
});
it("should accept a '--recurse' option and return an object with a 'recurse' property", function() {
opts.parse(['--recurse']);
var r = opts.get();
expect(r.recurse).toEqual(true);
});
it("should accept a '-h' option and return an object with a 'help' property", function() {
opts.parse(['-h']);
var r = opts.get();
expect(r.help).toEqual(true);
});
it("should accept a '--help' option and return an object with a 'help' property", function() {
opts.parse(['--help']);
var r = opts.get();
expect(r.help).toEqual(true);
});
it("should accept a '-X' option and return an object with a 'explain' property", function() {
opts.parse(['-X']);
var r = opts.get();
expect(r.explain).toEqual(true);
});
it("should accept a '--explain' option and return an object with a 'explain' property", function() {
opts.parse(['--explain']);
var r = opts.get();
expect(r.explain).toEqual(true);
});
it("should accept a '-q' option and return an object with a 'query' property", function() {
opts.parse(['-q', 'foo=bar&fab=baz']);
var r = opts.get();
expect(r.query).toEqual('foo=bar&fab=baz');
});
it("should accept a '--query' option and return an object with a 'query' property", function() {
opts.parse(['--query', 'foo=bar&fab=baz']);
var r = opts.get();
expect(r.query).toEqual('foo=bar&fab=baz');
});
it("should accept a '-t' option and return an object with a 'tutorials' property", function() {
opts.parse(['-d', 'mytutorials']);
var r = opts.get();
expect(r.destination).toEqual('mytutorials');
});
it("should accept a '--tutorials' option and return an object with a 'tutorials' property", function() {
opts.parse(['--tutorials', 'mytutorials']);
var r = opts.get();
expect(r.tutorials).toEqual('mytutorials');
});
it("should accept a naked option (i.e. no '-') and return an object with a '_' pproperty", function() {
opts.parse(['myfile1', 'myfile2']);
var r = opts.get();
expect(r._).toEqual(['myfile1', 'myfile2']);
});
it("should accept a '--verbose' option and return an object with a 'verbose' property", function() {
opts.parse(['--verbose']);
var r = opts.get();
expect(r.verbose).toEqual(true);
});
it("should accept a '--coffee' option and return an object with a 'coffee' property", function() {
opts.parse(['--coffee']);
var r = opts.get();
expect(r.coffee).toEqual(true);
});
it("should accept a '--match' option and return an object with a 'match' property", function() {
opts.parse(['--match', '.*tag']);
var r = opts.get();
expect(r.match).toEqual('.*tag');
});
it("should accept a multiple '--match' options and return an object with a 'match' property", function() {
opts.parse(['--match', '.*tag', '--match', 'parser']);
var r = opts.get();
expect(r.match).toEqual(['.*tag', 'parser']);
});
//TODO: tests for args that must have values
});
});

View File

@ -0,0 +1,54 @@
describe("jsdoc/src/handlers", function() {
var jsdoc = {src: { parser: require('jsdoc/src/parser')}},
testParser = new jsdoc.src.parser.Parser(),
handlers = require('jsdoc/src/handlers');
handlers.attachTo(testParser);
it("should exist", function() {
expect(handlers).toBeDefined();
expect(typeof handlers).toEqual("object");
});
it("should export an 'attachTo' function", function() {
expect(handlers.attachTo).toBeDefined();
expect(typeof handlers.attachTo).toEqual("function");
});
describe("attachTo", function() {
it("should attach a 'jsDocCommentFound' handler to the parser", function() {
var callbacks = testParser.__bindings['jsdocCommentFound'];
expect(callbacks).toBeDefined();
expect(callbacks.length).toEqual(1);
expect(typeof callbacks[0].handler).toEqual("function");
});
it("should attach a 'symbolFound' handler to the parser", function() {
var callbacks = testParser.__bindings['symbolFound'];
expect(callbacks).toBeDefined();
expect(callbacks.length).toEqual(1);
expect(typeof callbacks[0].handler).toEqual("function");
});
it("should attach a 'fileComplete' handler to the parser", function() {
var callbacks = testParser.__bindings['fileComplete'];
expect(callbacks).toBeDefined();
expect(callbacks.length).toEqual(1);
expect(typeof callbacks[0].handler).toEqual("function");
});
});
describe("jsdocCommentFound handler", function() {
var sourceCode = 'javascript:/** @name bar */',
result = testParser.parse(sourceCode);
it("should create a doclet for comments with '@name' tags", function() {
expect(result.length).toEqual(1);
expect(result[0].name).toEqual('bar');
});
});
describe("symbolFound handler", function() {
//TODO
});
});

View File

@ -0,0 +1,44 @@
describe("jsdoc/src/parser", function() {
var jsdoc = {src: { parser: require('jsdoc/src/parser')}};
it("should exist", function() {
expect(jsdoc.src.parser).toBeDefined();
expect(typeof jsdoc.src.parser).toEqual("object");
});
it("should export a 'Parser' constructor", function() {
expect(jsdoc.src.parser.Parser).toBeDefined();
expect(typeof jsdoc.src.parser.Parser).toEqual("function");
});
describe("Parser", function() {
it("should have a 'parse' function", function() {
expect(jsdoc.src.parser.Parser.prototype.parse).toBeDefined();
expect(typeof jsdoc.src.parser.Parser.prototype.parse).toEqual("function");
});
it("should have a 'results' function", function() {
expect(jsdoc.src.parser.Parser.prototype.results).toBeDefined();
expect(typeof jsdoc.src.parser.Parser.prototype.results).toEqual("function");
});
describe("parse", function() {
var parser = new jsdoc.src.parser.Parser();
it("should fire 'jsdocCommentFound' events when parsing source containing jsdoc comments", function() {
var spy = jasmine.createSpy(),
sourceCode = 'javascript:/** @name bar */';
parser.on('jsdocCommentFound', spy).parse(sourceCode);
expect(spy).toHaveBeenCalled();
expect(spy.mostRecentCall.args[0].comment).toEqual("/** @name bar */");
});
it("should fire 'symbolFound' events when parsing source containing named symbols", function() {
var spy = jasmine.createSpy(),
sourceCode = 'javascript:var foo = 1';
parser.on('symbolFound', spy).parse(sourceCode);
expect(spy).toHaveBeenCalled();
});
});
});
});

View File

@ -0,0 +1,17 @@
describe("jsdoc/src/scanner", function() {
var scanner = new (require('jsdoc/src/scanner').Scanner)(),
includeMatch = new RegExp(".+\\.js(doc)?$"),
excludeMatch = new RegExp("(^|\\/)_"),
sourceFiles = scanner.scan([__dirname+'/test/fixtures/src/'], 3, includeMatch, excludeMatch);
sourceFiles = sourceFiles.map(function($) {
return $.replace(__dirname, '');
});
it("should return the correct source files", function() {
expect(sourceFiles.length).toEqual(3);
expect(sourceFiles.indexOf("/test/fixtures/src/one.js")).toBeGreaterThan(-1);
expect(sourceFiles.indexOf("/test/fixtures/src/two.js")).toBeGreaterThan(-1);
expect(sourceFiles.indexOf("/test/fixtures/src/dir1/three.js")).toBeGreaterThan(-1);
});
});

View File

@ -0,0 +1,3 @@
describe("jsdoc/tag/dictionary", function() {
//TODO
});

View File

@ -0,0 +1,3 @@
describe("jsdoc/tag/type", function() {
//TODO
});

View File

@ -0,0 +1,3 @@
describe("jsdoc/tag/validator", function() {
//TODO
});

View File

@ -0,0 +1,101 @@
describe("common/dumper", function() {
var common = {dumper: require('jsdoc/util/dumper')};
it("should exist", function() {
expect(common.dumper).toBeDefined();
expect(typeof common.dumper).toEqual("object");
});
it("should export a 'dump' function", function() {
expect(common.dumper.dump).toBeDefined();
expect(typeof common.dumper.dump).toEqual("function");
});
it("can dump string values", function() {
expect(common.dumper.dump('hello')).toEqual('"hello"');
});
it("escapes double quotes in string values", function() {
expect(common.dumper.dump('hello "world"')).toEqual('"hello \\"world\\""', 'Double quotes should be escaped.');
});
it("escapes newlines in string values", function() {
expect(common.dumper.dump('hello\nworld')).toEqual('"hello\\nworld"', 'Newlines should be escaped.');
});
it("can dump number values", function() {
expect(common.dumper.dump(1)).toEqual('1');
expect(common.dumper.dump(0.1)).toEqual('0.1');
});
it("can dump boolean values", function() {
expect(common.dumper.dump(true)).toEqual('true');
expect(common.dumper.dump(false)).toEqual('false');
});
it("can dump null values", function() {
expect(common.dumper.dump(null)).toEqual('null');
});
it("can dump undefined values", function() {
expect(common.dumper.dump(undefined)).toEqual('undefined');
});
it("can dump regex values", function() {
expect(common.dumper.dump(/^[Ff]oo$/gi)).toEqual('<RegExp /^[Ff]oo$/gi>');
});
it("can dump date values", function() {
expect(common.dumper.dump(new Date('January 1, 1901 GMT'))).toEqual('<Date Tue, 01 Jan 1901 00:00:00 GMT>');
});
it("can dump function values", function() {
expect(common.dumper.dump(function myFunc(){})).toEqual('<Function myFunc>');
expect(common.dumper.dump(function(){})).toEqual('<Function>');
});
it("can dump array values", function() {
var actual = common.dumper.dump(["hello", "world"]),
expected = '[\n "hello",\n "world"\n]';
expect(actual).toEqual(expected);
});
it("can dump simple object values", function() {
var actual = common.dumper.dump({hello: "world"}),
expected = '{\n "hello": "world"\n}';
expect(actual).toEqual(expected);
});
it("can dump constructed instance values, not displaying prototype members", function() {
function Foo(name){ this.name = name; }
Foo.prototype.sayHello = function(){};
var actual = common.dumper.dump(new Foo('hello')),
expected = '{\n "name": "hello"\n}';
expect(actual).toEqual(expected);
});
it("can dump complex mixed values", function() {
function Foo(){}
var actual = common.dumper.dump(
[undefined, null, new Foo(), 1, true, 'hello\n"world', new Error('oops'), /foo/gi, new Date('December 26, 2010 GMT'), {f: function myFunc(){}, o: {a:1}}]
),
expected = '[\n undefined,\n null,\n {\n },\n 1,\n true,\n "hello\\n\\"world",\n {\n "message": "oops"\n },\n <RegExp /foo/gi>,\n <Date Sun, 26 Dec 2010 00:00:00 GMT>,\n {\n "f": <Function myFunc>,\n "o": {\n "a": 1\n }\n }\n]';
expect(actual).toEqual(expected);
});
it("doesn't crash on circular references", function() {
var a = {};
a.b = a;
var actual = common.dumper.dump(a),
expected = '{\n "b": <CircularRef>\n}';
expect(actual).toEqual(expected);
});
});

View File

@ -0,0 +1,141 @@
describe("jsdoc/util/templateHelper", function() {
var helper = require('jsdoc/util/templateHelper');
helper.registerLink('test', 'path/to/test.html');
helper.registerLink('test."long blah"/blah', 'path/to/test_long_blah_blah.html');
it("should exist", function() {
expect(helper).toBeDefined();
expect(typeof helper).toEqual('object');
});
it("should export a 'resolveLinks' function", function() {
expect(helper.resolveLinks).toBeDefined();
expect(typeof helper.resolveLinks).toEqual("function");
});
it("should export a 'createLink' function", function() {
expect(helper.createLink).toBeDefined();
expect(typeof helper.createLink).toEqual("function");
});
it("should export a 'setTutorials' function", function() {
expect(helper.setTutorials).toBeDefined();
expect(typeof helper.setTutorials).toEqual("function");
});
it("should export a 'toTutorial' function", function() {
expect(helper.toTutorial).toBeDefined();
expect(typeof helper.toTutorial).toEqual("function");
});
it("should export a 'tutorialToUrl' function", function() {
expect(helper.tutorialToUrl).toBeDefined();
expect(typeof helper.tutorialToUrl).toEqual("function");
});
describe("resolveLinks", function() {
it('should translate {@link test} into a HTML link.', function() {
var input = 'This is a {@link test}.',
output = helper.resolveLinks(input);
expect(output).toEqual('This is a <a href="path/to/test.html">test</a>.');
});
it('should translate {@link test."long blah"/blah} into a HTML link.', function() {
var input = 'This is a {@link test."long blah"/blah}.',
output = helper.resolveLinks(input);
expect(output).toEqual('This is a <a href="path/to/test_long_blah_blah.html">test."long blah"/blah</a>.');
});
it('should translate {@link unknown} into a simple text.', function() {
var input = 'This is a {@link unknown}.',
output = helper.resolveLinks(input);
expect(output).toEqual('This is a unknown.');
});
it('should translate {@link test} into a HTML links multiple times.', function() {
var input = 'This is a {@link test} and {@link test}.',
output = helper.resolveLinks(input);
expect(output).toEqual('This is a <a href="path/to/test.html">test</a> and <a href="path/to/test.html">test</a>.');
});
it('should translate [hello there]{@link test} into a HTML link with the custom content.', function() {
var input = 'This is a [hello there]{@link test}.',
output = helper.resolveLinks(input);
expect(output).toEqual('This is a <a href="path/to/test.html">hello there</a>.');
});
it('should ignore [hello there].', function() {
var input = 'This is a [hello there].',
output = helper.resolveLinks(input);
expect(output).toEqual(input);
});
});
describe("createLink", function() {
it('should create a url for a simple global.', function() {
var mockDoclet = {
kind: 'function',
longname: 'foo',
name: 'foo'
},
url = helper.createLink(mockDoclet);
expect(url).toEqual('global.html#foo');
});
it('should create a url for a namespace.', function() {
var mockDoclet = {
kind: 'namespace',
longname: 'foo',
name: 'foo'
},
url = helper.createLink(mockDoclet);
expect(url).toEqual('foo.html');
});
it('should create a url for a member of a namespace.', function() {
var mockDoclet = {
kind: 'function',
longname: 'ns.foo',
name: 'foo',
memberof: 'ns'
},
url = helper.createLink(mockDoclet);
expect(url).toEqual('ns.html#foo');
});
it('should create a url for a member of a nested namespace.', function() {
var mockDoclet = {
kind: 'function',
longname: 'ns1.ns2.foo',
name: 'foo',
memberof: 'ns1.ns2'
},
url = helper.createLink(mockDoclet);
expect(url).toEqual('ns1.ns2.html#foo');
});
it('should create a url for a name with invalid characters using a digest.', function() {
var mockDoclet = {
kind: 'function',
longname: 'ns1."!"."*foo"',
name: '"*foo"',
memberof: 'ns1."!"'
},
url = helper.createLink(mockDoclet);
expect(url).toEqual('9305caaec5.html#"*foo"');
});
});
//TODO: tests for tutorial functions?
});

Some files were not shown because too many files have changed in this diff Show More