Plugin tests

Tests for the plugin framework as well as for the plugins shipping with JSDoc
This commit is contained in:
Jannon 2012-05-06 00:43:22 -07:00
parent 034205baa8
commit a6f68d838c
17 changed files with 273 additions and 70 deletions

112
jsdoc.js
View File

@ -34,7 +34,7 @@ load(__dirname + '/lib/rhino-shim.js');
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
/** Data representing the environment in which this app is running.
@namespace
*/
@ -44,20 +44,20 @@ env = {
start: new Date(),
finish: null
},
/**
The command line arguments passed into jsdoc.
@type Array
*/
args: Array.prototype.slice.call(args, 0),
/**
The parsed JSON data from the configuration file.
@type Object
*/
conf: {},
/**
The command line arguments, parsed into a key/value hash.
@type Object
@ -79,12 +79,13 @@ app = {
}
try { main(); }
catch(e) {
if (e.rhinoException != null) {
catch(e) {
if (e.rhinoException != null) {
e.rhinoException.printStackTrace();
}
else throw e;
}
} else {
throw e;
}
}
finally { env.run.finish = new Date(); }
/** Print string/s out to the console.
@ -119,7 +120,7 @@ function include(filepath) {
}
}
/**
/**
Cause the VM running jsdoc to exit running.
@param {number} [n = 0] The exit status.
*/
@ -128,6 +129,33 @@ function exit(n) {
java.lang.System.exit(n);
}
function installPlugins(plugins, p) {
var dictionary = require('jsdoc/tag/dictionary'),
parser = p || app.jsdoc.parser;
// allow user-defined plugins to...
for (var i = 0, leni = plugins.length; i < leni; i++) {
var plugin = require(plugins[i]);
//...register event handlers
if (plugin.handlers) {
for (var eventName in plugin.handlers) {
parser.on(eventName, plugin.handlers[eventName]);
}
}
//...define tags
if (plugin.defineTags) {
plugin.defineTags(dictionary);
}
//...add a node visitor
if (plugin.nodeVisitor) {
parser.addNodeVisitor(plugin.nodeVisitor);
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
@ -144,11 +172,10 @@ function main() {
}
},
resolver,
dictionary = require('jsdoc/tag/dictionary'),
fs = require('fs');
env.opts = jsdoc.opts.parser.parse(env.args);
try {
env.conf = JSON.parse(
fs.readFileSync( env.opts.configure || __dirname + '/conf.json' )
@ -162,10 +189,10 @@ function main() {
env.conf = JSON.parse(example);
}
catch(e) {
throw('Configuration file cannot be evaluated. ' + e);
throw('Configuration file cannot be evaluated. ' + e);
}
}
// allow to pass arguments from configuration file
if (env.conf.opts) {
for (var opt in env.conf.opts) {
@ -179,47 +206,26 @@ function main() {
env.opts._ = env.conf.opts._.concat( env.opts._ );
}
}
if (env.opts.query) {
env.opts.query = require('common/query').toObject(env.opts.query);
}
// which version of javascript will be supported? (rhino only)
if (typeof version === 'function') {
version(env.conf.jsVersion || 180);
}
if (env.opts.help) {
console.log( jsdoc.opts.parser.help() );
exit(0);
}
else if (env.opts.test) {
} else if (env.opts.test) {
include('test/runner.js');
exit(0);
}
// allow user-defined plugins to...
if (env.conf.plugins) {
for (var i = 0, leni = env.conf.plugins.length; i < leni; i++) {
var plugin = require(env.conf.plugins[i]);
//...register event handlers
if (plugin.handlers) {
for (var eventName in plugin.handlers) {
app.jsdoc.parser.on(eventName, plugin.handlers[eventName]);
}
}
//...define tags
if (plugin.defineTags) {
plugin.defineTags(dictionary);
}
//...add a node visitor
if (plugin.nodeVisitor) {
app.jsdoc.parser.addNodeVisitor(plugin.nodeVisitor);
}
}
installPlugins(env.conf.plugins);
}
// any source file named package.json is treated special
@ -231,25 +237,25 @@ function main() {
}
if (env.opts._.length > 0) { // are there any files to scan and parse?
var includeMatch = (env.conf.source && env.conf.source.includePattern)? new RegExp(env.conf.source.includePattern) : null,
excludeMatch = (env.conf.source && env.conf.source.excludePattern)? new RegExp(env.conf.source.excludePattern) : null;
sourceFiles = app.jsdoc.scanner.scan(env.opts._, (env.opts.recurse? 10 : undefined), includeMatch, excludeMatch);
require('jsdoc/src/handlers').attachTo(app.jsdoc.parser);
docs = app.jsdoc.parser.parse(sourceFiles, env.opts.encoding);
//The files are ALWAYS useful for the templates to have
//If there is no package.json, just create an empty package
var packageDocs = new (require('jsdoc/package').Package)(packageJson);
packageDocs.files = sourceFiles || [];
docs.push(packageDocs);
function indexAll(docs) {
var lookupTable = {};
docs.forEach(function(doc) {
if ( !lookupTable.hasOwnProperty(doc.longname) ) {
lookupTable[doc.longname] = [];
@ -258,12 +264,12 @@ function main() {
});
docs.index = lookupTable;
}
indexAll(docs);
require('jsdoc/augment').addInherited(docs);
require('jsdoc/borrow').resolveBorrows(docs);
if (env.opts.explain) {
console.log(docs);
exit(0);
@ -279,7 +285,7 @@ function main() {
}
env.opts.template = env.opts.template || 'templates/default';
// should define a global "publish" function
include(env.opts.template + '/publish.js');

View File

@ -1,10 +1,10 @@
/**
@overview Demonstrate how to modify the source code before the parser sees it.
@module plugins/comentConvert
@module plugins/commentConvert
@author Michael Mathews <micmath@gmail.com>
*/
exports.handlers = {
///
/// Convert ///-style comments into jsdoc comments.

View File

@ -8,6 +8,7 @@
exports.handlers = {
/**
Translate HTML tags in descriptions into safe entities.
Replaces <, & and newlines
*/
newDoclet: function(e) {
if (e.doclet.description) {

View File

@ -6,12 +6,12 @@
exports.handlers = {
///
/// Remove rails tags from the source input (e.g. <% foo bar %>)
/// @param e
/// @param e.filename
/// @param e.source
///
/**
* Remove rails tags from the source input (e.g. <% foo bar %>)
* @param e
* @param e.filename
* @param e.source
*/
beforeParse: function(e) {
if (e.filename.match(/\.erb$/)) {
e.source = e.source.replace(/<%.*%>/g, "");

View File

@ -8,35 +8,36 @@ exports.handlers = {
Support @source tag. Expected value like:
{ "filename": "myfile.js", "lineno": 123 }
Modifies the corresponding meta values on the given doclet.
@source { "filename": "sourcetag.js", "lineno": 13 }
*/
newDoclet: function(e) {
var tags = e.doclet.tags,
tag,
value;
//console.log(e.doclet);
// any user-defined tags in this doclet?
// any user-defined tags in this doclet?
if (typeof tags !== 'undefined') {
// only interested in the @source tags
tags = tags.filter(function($) {
return $.title === 'source';
});
if (tags.length) {
// take the first one
tag = tags[0];
try {
value = JSON.parse(tag.value);
}
catch(e) {
throw new Error('@source tag expects a valid JSON value, like { "filename": "myfile.js", "lineno": 123 }.');
}
!e.doclet.meta && (e.doclet.meta = {});
e.doclet.meta.filename = value.filename || '';
e.doclet.meta.lineno = value.lineno || '';
}
}
}
}
};

View File

@ -0,0 +1,20 @@
/**
@overview Strips the rails template tags from a js.erb file
@module plugins/railsTemplate
@author Jannon Frank <jannon@jannon.net>
*/
exports.handlers = {
/**
* Remove rails tags from the source input (e.g. <% foo bar %>)
* @param e
* @param e.filename
* @param e.source
*/
beforeParse: function(e) {
if (e.filename.match(/\.erb$/)) {
e.source = e.source.replace(/<%.*%> /g, "");
}
}
};

View File

@ -0,0 +1,13 @@
describe("commentConvert plugin", function() {
var parser = new (require("jsdoc/src/parser")).Parser(),
plugin = require('plugins/commentConvert'),
docSet;
installPlugins(['plugins/commentConvert'], parser);
docSet = jasmine.getDocSetFromFile("plugins/commentConvert.js", parser);
it("should convert '///-style comments into jsdoc comments", function() {
var doclet = docSet.getByLongname("module:plugins/commentConvert.handlers.beforeParse");
expect(doclet.length).toEqual(1);
});
});

View File

@ -0,0 +1,13 @@
describe("escapeHtml plugin", function() {
var parser = new (require("jsdoc/src/parser")).Parser(),
plugin = require('plugins/escapeHtml'),
docSet;
installPlugins(['plugins/escapeHtml'], parser);
docSet = jasmine.getDocSetFromFile("plugins/escapeHtml.js", parser);
it("should escape '&', '<' and newlines in doclet descriptions", function() {
var doclet = docSet.getByLongname("module:plugins/escapeHtml.handlers.newDoclet");
expect(doclet[0].description).toEqual("Translate HTML tags in descriptions into safe entities.<br> Replaces &lt;, &amp; and newlines");
});
});

View File

@ -0,0 +1,22 @@
/**
@overview Translate doclet descriptions from MarkDown into HTML.
@module plugins/markdown
@author Michael Mathews <micmath@gmail.com>
*/
var mdParser = require('evilstreak/markdown');
exports.handlers = {
/**
Translate markdown syntax in a new doclet's description into HTML. Is run
by JSDoc 3 whenever a "newDoclet" event fires.
*/
newDoclet: function(e) {
if (e.doclet.description) {
e.doclet.description = mdParser.toHTML(e.doclet.description)
.replace( /&amp;/g, "&" ) // because markdown escapes these
.replace( /&lt;/g, "<" )
.replace( /&gt;/g, ">" );
}
}
};

View File

@ -0,0 +1,15 @@
describe("railsTemplate plugin", function() {
var parser = new (require("jsdoc/src/parser")).Parser(),
plugin = require('plugins/railsTemplate');
installPlugins(['plugins/railsTemplate'], parser);
require('jsdoc/src/handlers').attachTo(parser);
it("should remove <% %> rails template tags from the source of *.erb files", function() {
var path = require("path"),
docSet = parser.parse([path.join(__dirname, "plugins/test/fixtures/railsTemplate.js.erb")]);
expect(docSet[2].description).toEqual("Remove rails tags from the source input (e.g. )");
});
});

View File

@ -0,0 +1,13 @@
describe("shout plugin", function() {
var parser = new (require("jsdoc/src/parser")).Parser(),
plugin = require('plugins/shout'),
docSet;
installPlugins(['plugins/shout'], parser);
docSet = jasmine.getDocSetFromFile("plugins/shout.js", parser);
it("should make the description uppercase", function() {
var doclet = docSet.getByLongname("module:plugins/shout.handlers.newDoclet");
expect(doclet[0].description).toEqual("MAKE YOUR DESCRIPTIONS MORE SHOUTIER.");
});
});

View File

@ -0,0 +1,15 @@
describe("sourcetag plugin", function() {
var parser = new (require("jsdoc/src/parser")).Parser(),
plugin = require('plugins/sourcetag'),
docSet;
installPlugins(['plugins/sourcetag'], parser);
docSet = jasmine.getDocSetFromFile("plugins/sourcetag.js", parser);
it("should set the lineno and filename of the doclet's meta property", function() {
var doclet = docSet.getByLongname("module:plugins/sourcetag.handlers.newDoclet");
expect(doclet[0].meta).toBeDefined();
expect(doclet[0].meta.filename).toEqual("sourcetag.js");
expect(doclet[0].meta.lineno).toEqual(13);
});
});

10
test/fixtures/plugins.js vendored Normal file
View File

@ -0,0 +1,10 @@
/**
* @name virtual
*/
var foo = "bar";
/**
* @foo bar
*/
var test = "tada";

23
test/fixtures/testPlugin1.js vendored Normal file
View File

@ -0,0 +1,23 @@
exports.handlers = {
fileBegin: jasmine.createSpy('fileBegin'),
beforeParse: jasmine.createSpy('beforeParse'),
jsdocCommentFound: jasmine.createSpy('jsdocCommentFound'),
symbolFound: jasmine.createSpy('symbolFound'),
newDoclet: jasmine.createSpy('newDoclet'),
fileComplete: jasmine.createSpy('fileComplete')
};
exports.defineTags = function(dictionary) {
dictionary.defineTag("foo", {
canHaveName: true,
onTagged: function(doclet, tag) {
doclet.foo = true;
}
});
};
exports.nodeVisitor = {
visitNode: jasmine.createSpy("plugin 1 visitNode").andCallFake(function(node, e, parser, currentSourceName) {
e.stopPropagation = true;
})
};

12
test/fixtures/testPlugin2.js vendored Normal file
View File

@ -0,0 +1,12 @@
exports.handlers = {
fileBegin: jasmine.createSpy('fileBegin'),
beforeParse: jasmine.createSpy('beforeParse'),
jsdocCommentFound: jasmine.createSpy('jsdocCommentFound'),
symbolFound: jasmine.createSpy('symbolFound'),
newDoclet: jasmine.createSpy('newDoclet'),
fileComplete: jasmine.createSpy('fileComplete')
};
exports.nodeVisitor = {
visitNode: jasmine.createSpy("plugin 2 visitNode")
};

View File

@ -1,9 +1,8 @@
exports.getDocSetFromFile = function(filename) {
exports.getDocSetFromFile = function(filename, parser) {
var sourceCode = readFile(__dirname + '/' + filename),
testParser,
testParser = parser || new (require('jsdoc/src/parser')).Parser(),
doclets;
testParser = new (require('jsdoc/src/parser')).Parser();
require('jsdoc/src/handlers').attachTo(testParser);
doclets = testParser.parse('javascript:' + sourceCode);

View File

@ -0,0 +1,40 @@
describe("plugins", function() {
installPlugins(['test/fixtures/testPlugin1', 'test/fixtures/testPlugin2']);
var plugin1 = require('test/fixtures/testPlugin1'),
plugin2 = require('test/fixtures/testPlugin2'),
docSet;
docSet = jasmine.getDocSetFromFile("test/fixtures/plugins.js", app.jsdoc.parser);
it("should fire the plugin's event handlers", function() {
expect(plugin1.handlers.fileBegin).toHaveBeenCalled();
expect(plugin1.handlers.beforeParse).toHaveBeenCalled();
expect(plugin1.handlers.jsdocCommentFound).toHaveBeenCalled();
expect(plugin1.handlers.symbolFound).toHaveBeenCalled();
expect(plugin1.handlers.newDoclet).toHaveBeenCalled();
expect(plugin1.handlers.fileComplete).toHaveBeenCalled();
expect(plugin2.handlers.fileBegin).toHaveBeenCalled();
expect(plugin2.handlers.beforeParse).toHaveBeenCalled();
expect(plugin2.handlers.jsdocCommentFound).toHaveBeenCalled();
expect(plugin2.handlers.symbolFound).toHaveBeenCalled();
expect(plugin2.handlers.newDoclet).toHaveBeenCalled();
expect(plugin2.handlers.fileComplete).toHaveBeenCalled();
});
it("should add the plugin's tag definitions to the dictionary", function() {
var test = docSet.getByLongname("test");
expect(test[0].longname).toEqual("test");
expect(test[0].foo).toEqual(true);
});
it("should call the plugin's visitNode function", function() {
expect(plugin1.nodeVisitor.visitNode).toHaveBeenCalled();
});
it("should not call a second plugin's visitNode function if the first stopped propagation", function() {
expect(plugin2.nodeVisitor.visitNode).not.toHaveBeenCalled();
});
});