diff --git a/rhino_modules/jsdoc/augment.js b/rhino_modules/jsdoc/augment.js index f20202f6..291cc6e4 100644 --- a/rhino_modules/jsdoc/augment.js +++ b/rhino_modules/jsdoc/augment.js @@ -96,7 +96,8 @@ var doop = require("jsdoc/util/doop").doop; this.visited[key] = true; if (!(key in this.dependencies)) { - throw new Error("Missing dependency: " + key); + require('jsdoc/util/error').handle( new Error("Missing dependency: " + key) ); + return; } for (var path in this.dependencies[key]) { if ( hasOwnProp.call(this.dependencies[key], path) ) { diff --git a/rhino_modules/jsdoc/src/parser.js b/rhino_modules/jsdoc/src/parser.js index 17a3a884..df1eb261 100644 --- a/rhino_modules/jsdoc/src/parser.js +++ b/rhino_modules/jsdoc/src/parser.js @@ -123,7 +123,7 @@ function pretreat(code) { // merge adjacent doclets .replace(/\*\/\/\*\*+/g, '@also') // make lent objectliterals documentable by giving them a dummy name - .replace(/(\/\*\*[^\*\/]*?@lends\b[^\*\/]*?\*\/\s*)\{/g, '$1 ____ = {') // like return @lends { + .replace(/(\/\*\*[^\*\/]*?[\*\s]*@lends\s(?:[^\*]|\*(?!\/))*\*\/\s*)\{/g, '$1 ____ = {') // like return @lends { .replace(/(\/\*\*[^\*\/]*?@lends\b[^\*\/]*?\*\/)(\s*)return(\s*)\{/g, '$2$3 return $1 ____ = {'); // like @lends return { } diff --git a/rhino_modules/jsdoc/tag.js b/rhino_modules/jsdoc/tag.js index 86153f97..d1e06f2d 100644 --- a/rhino_modules/jsdoc/tag.js +++ b/rhino_modules/jsdoc/tag.js @@ -95,15 +95,5 @@ exports.Tag = function(tagTitle, tagBody, meta) { } } - // validate the tag. in lenient mode, log a warning; otherwise, throw an exception. - try { - jsdoc.tag.validator.validate(this, meta); - } - catch (e) { - if (env.opts.lenient) { - console.log(e); - } else { - throw e; - } - } + jsdoc.tag.validator.validate(this, meta); }; diff --git a/rhino_modules/jsdoc/tag/validator.js b/rhino_modules/jsdoc/tag/validator.js index 928bfdd7..a21a7a10 100644 --- a/rhino_modules/jsdoc/tag/validator.js +++ b/rhino_modules/jsdoc/tag/validator.js @@ -35,17 +35,17 @@ exports.validate = function(tag, meta) { var tagDef = dictionary.lookUp(tag.title); if (!tagDef && !env.conf.tags.allowUnknownTags) { - throw new UnknownTagError(tag.title, meta); + require('jsdoc/util/error').handle( new UnknownTagError(tag.title, meta) ); } if (!tag.text) { if (tagDef.mustHaveValue) { - throw new TagValueRequiredError(tag.title, meta); + require('jsdoc/util/error').handle( new TagValueRequiredError(tag.title, meta) ); } } else { if (tagDef.mustNotHaveValue) { - throw new TagValueNotPermittedError(tag.title, meta); + require('jsdoc/util/error').handle( new TagValueNotPermittedError(tag.title, meta) ); } } }; diff --git a/rhino_modules/jsdoc/tutorial/resolver.js b/rhino_modules/jsdoc/tutorial/resolver.js index 3cbc05d3..d83e9417 100644 --- a/rhino_modules/jsdoc/tutorial/resolver.js +++ b/rhino_modules/jsdoc/tutorial/resolver.js @@ -32,7 +32,7 @@ exports.root = new tutorial.Tutorial('', ''); /** Additional instance method for root node. @param {string} name - Tutorial name. - @reutrn {tutorial.Tutorial} Tutorial instance. + @return {tutorial.Tutorial} Tutorial instance. */ exports.root.getByName = function(name) { return tutorials[name]; @@ -93,7 +93,6 @@ exports.load = function(path) { }; /** Resolves hierarchical structure. - @param {object} map - Contents map. */ exports.resolve = function() { var item, @@ -118,13 +117,12 @@ exports.resolve = function() { // add children if (item.children) { item.children.forEach(function(child) { - // I really didn't want to throw you an exception in most cases - // but now, user, you pissed me off ;) if (!(child in tutorials)) { - throw new Error("Missing child tutorial: " + child); + require('jsdoc/util/error').handle( new Error("Missing child tutorial: " + child) ); + } + else { + tutorials[child].setParent(current); } - - tutorials[child].setParent(current); }); } } diff --git a/rhino_modules/jsdoc/util/error.js b/rhino_modules/jsdoc/util/error.js new file mode 100644 index 00000000..28e7d112 --- /dev/null +++ b/rhino_modules/jsdoc/util/error.js @@ -0,0 +1,32 @@ +/*global env: true */ +/** + Helper functions for handling errors. + @module jsdoc/util/error + */ + +/** + Handle an exception appropriately based on whether lenient mode is enabled: + + + If lenient mode is enabled, log the exception to the console. + + If lenient mode is not enabled, re-throw the exception. + @param {Error} e - The exception to handle. + @exception {Error} Re-throws the original exception unless lenient mode is enabled. + @memberof module:jsdoc/util/error + */ +exports.handle = function(e) { + var msg; + + if (env.opts.lenient) { + msg = e.message || JSON.stringify(e); + + // include the error type if it's an Error object + if (e instanceof Error) { + msg = e.constructor.name + ": " + msg; + } + + console.log(msg); + } + else { + throw e; + } +}; diff --git a/rhino_modules/jsdoc/util/templateHelper.js b/rhino_modules/jsdoc/util/templateHelper.js index 2589b171..53201528 100644 --- a/rhino_modules/jsdoc/util/templateHelper.js +++ b/rhino_modules/jsdoc/util/templateHelper.js @@ -65,6 +65,7 @@ exports.registerLink = function(longname, url) { function toLink(longname, content) { if (!longname) { + // if this happens, there's something wrong with the caller itself; the user can't fix this throw new Error('Missing required parameter: url'); } @@ -96,7 +97,8 @@ function toLink(longname, content) { var toTutorial = exports.toTutorial = function(tutorial, content) { if (!tutorial) { - throw new Error('Missing required parameter: tutorial'); + require('jsdoc/util/error').handle( new Error('Missing required parameter: tutorial') ); + return; } var node = tutorials.getByName(tutorial); @@ -155,7 +157,8 @@ exports.tutorialToUrl = function(tutorial) { var node = tutorials.getByName(tutorial); // no such tutorial if (!node) { - throw new Error('No such tutorial: '+tutorial); + require('jsdoc/util/error').handle( new Error('No such tutorial: '+tutorial) ); + return; } return 'tutorial-'+strToFilename(node.name)+exports.fileExtension; diff --git a/test/fixtures/augmentstag2.js b/test/fixtures/augmentstag2.js new file mode 100644 index 00000000..04bf1c23 --- /dev/null +++ b/test/fixtures/augmentstag2.js @@ -0,0 +1,6 @@ +// Test for @augments tags that refer to undefined symbols +/** + * @constructor + * @extends Foo + */ +function Bar() {} diff --git a/test/fixtures/lends3.js b/test/fixtures/lends3.js new file mode 100644 index 00000000..13c11a31 --- /dev/null +++ b/test/fixtures/lends3.js @@ -0,0 +1,18 @@ +/** @class */ +var Person = makeClass( + /** + * @lends Person# + */ + { + /** Set up initial values. */ + initialize: function(name) { + /** The name of the person. */ + this.name = name; + }, + + /** Speak a message. */ + say: function(message) { + return this.name + " says: " + message; + } + } +); \ No newline at end of file diff --git a/test/specs/documentation/lends.js b/test/specs/documentation/lends.js index 38938548..fe24a13c 100644 --- a/test/specs/documentation/lends.js +++ b/test/specs/documentation/lends.js @@ -34,6 +34,20 @@ describe("lends", function() { }); }); + describe("case that uses @lends in a multiline doclet", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/lends3.js'), + init = docSet.getByLongname('Person#initialize'), + name = docSet.getByLongname('Person#name'); + + it("The member should be documented as a member of the lendee", function() { + expect(init.length, 1); + }); + + it("The this member should be documented as a member of the lendee", function() { + expect(name.length, 1); + }); + }); + }); describe("when a documented member is inside an objlit associated with a @lends tag that has no value.", function() { diff --git a/test/specs/jsdoc/augment.js b/test/specs/jsdoc/augment.js index 7c636b7c..1a202b1e 100644 --- a/test/specs/jsdoc/augment.js +++ b/test/specs/jsdoc/augment.js @@ -1,3 +1,31 @@ +/*global describe: true, env: true, it: true */ describe("jsdoc/augment", function() { - //TODO -}); \ No newline at end of file + /*jshint evil: true */ + + // TODO: more tests + + var lenient = !!env.opts.lenient, + log = eval(console.log); + + function augmentMissingSymbol() { + var badDocSet = jasmine.getDocSetFromFile('test/fixtures/augmentstag2.js'); + } + + afterEach(function() { + env.opts.lenient = lenient; + console.log = log; + }); + + it("throws an error for missing dependencies if the lenient option is not enabled", function() { + env.opts.lenient = false; + + expect(augmentMissingSymbol).toThrow(); + }); + + it("does not throw an error for missing dependencies if the lenient option is enabled", function() { + console.log = function() {}; + env.opts.lenient = true; + + expect(augmentMissingSymbol).not.toThrow(); + }); +}); diff --git a/test/specs/jsdoc/tag.js b/test/specs/jsdoc/tag.js index eb701fab..af71b0a0 100644 --- a/test/specs/jsdoc/tag.js +++ b/test/specs/jsdoc/tag.js @@ -1,6 +1,11 @@ /*global describe: true, env: true, it: true */ describe("jsdoc/tag", function() { + /*jshint evil: true */ + // TODO: more tests + + var lenient = !!env.opts.lenient, + log = eval(console.log); function badTag() { var Tag = require("jsdoc/tag").Tag; @@ -8,29 +13,21 @@ describe("jsdoc/tag", function() { return tag; } - it("is strict, not lenient, by default", function() { - expect(badTag).toThrow(); - }); - - it("throws an exception for bad tags if the lenient option is not enabled", function() { - var lenient = !!env.opts.lenient; - - env.opts.lenient = false; - expect(badTag).toThrow(); - - env.opts.lenient = lenient; - }); - - it("doesn't throw an exception for bad tags if the lenient option is enabled", function() { - /*jshint evil: true */ - var lenient = !!env.opts.lenient, - log = eval(console.log); - console.log = function() {}; - - env.opts.lenient = true; - expect(badTag).not.toThrow(); - + afterEach(function() { env.opts.lenient = lenient; console.log = log; }); + + it("throws an exception for bad tags if the lenient option is not enabled", function() { + env.opts.lenient = false; + + expect(badTag).toThrow(); + }); + + it("doesn't throw an exception for bad tags if the lenient option is enabled", function() { + console.log = function() {}; + env.opts.lenient = true; + + expect(badTag).not.toThrow(); + }); }); \ No newline at end of file diff --git a/test/specs/jsdoc/tutorial.js b/test/specs/jsdoc/tutorial.js new file mode 100644 index 00000000..9330fa89 --- /dev/null +++ b/test/specs/jsdoc/tutorial.js @@ -0,0 +1,4 @@ +/*global describe: true, env: true, it: true */ +describe("jsdoc/tutorial", function() { + //TODO +}); \ No newline at end of file diff --git a/test/specs/jsdoc/tutorial/resolver.js b/test/specs/jsdoc/tutorial/resolver.js new file mode 100644 index 00000000..9892350e --- /dev/null +++ b/test/specs/jsdoc/tutorial/resolver.js @@ -0,0 +1,33 @@ +/*global describe: true, env: true, it: true */ +describe("jsdoc/tutorial/resolver", function() { + /*jshint evil: true */ + + // TODO: more tests + + var resolver = require('jsdoc/tutorial/resolver'), + lenient = !!env.opts.lenient, + log = eval(console.log); + + function missingTutorial() { + resolver.load(env.dirname + "/test/tutorials/incomplete"); + resolver.resolve(); + } + + afterEach(function() { + env.opts.lenient = lenient; + console.log = log; + }); + + it("throws an exception for missing tutorials if the lenient option is not enabled", function() { + env.opts.lenient = false; + + expect(missingTutorial).toThrow(); + }); + + it("doesn't throw an exception for missing tutorials if the lenient option is enabled", function() { + console.log = function() {}; + env.opts.lenient = true; + + expect(missingTutorial).not.toThrow(); + }); +}); \ No newline at end of file diff --git a/test/specs/jsdoc/util/doop.js b/test/specs/jsdoc/util/doop.js new file mode 100644 index 00000000..c0f53292 --- /dev/null +++ b/test/specs/jsdoc/util/doop.js @@ -0,0 +1,4 @@ +/*global describe: true, it: true */ +describe("jsdoc/util/doop", function() { + // TODO +}); \ No newline at end of file diff --git a/test/specs/jsdoc/util/error.js b/test/specs/jsdoc/util/error.js new file mode 100644 index 00000000..4d0a8981 --- /dev/null +++ b/test/specs/jsdoc/util/error.js @@ -0,0 +1,55 @@ +/*global describe: true, env: true, it: true */ +describe("jsdoc/util/error", function() { + var error = require('jsdoc/util/error'), + handle = error.handle; + + it("should exist", function() { + expect(error).toBeDefined(); + expect(typeof error).toEqual("object"); + }); + + it("should export a 'handle' function", function() { + expect(handle).toBeDefined(); + expect(typeof handle).toEqual("function"); + }); + + describe("handle", function() { + /*jshint evil: true */ + var lenient = !!env.opts.lenient, + log = eval(console.log); + + function handleError() { + handle( new Error("foo") ); + } + + function handleObject() { + handle( { foo: "bar", baz: "qux"} ); + } + + afterEach(function() { + env.opts.lenient = lenient; + console.log = log; + }); + + it("should re-throw errors by default", function() { + expect(handleError).toThrow(); + }); + + it("should re-throw errors if lenient mode is not enabled", function() { + env.opts.lenient = false; + + expect(handleError).toThrow(); + }); + + it("should not re-throw errors if lenient mode is enabled", function() { + env.opts.lenient = true; + console.log = function() {}; + + expect(handleError).not.toThrow(); + }); + + it("should still work if the 'e' param is not an instanceof Error", function() { + expect(handleObject).toThrow(); + }); + }); +}); diff --git a/test/specs/jsdoc/util/templateHelper.js b/test/specs/jsdoc/util/templateHelper.js index 530afb07..b3b546a7 100644 --- a/test/specs/jsdoc/util/templateHelper.js +++ b/test/specs/jsdoc/util/templateHelper.js @@ -1,3 +1,4 @@ +/*global describe: true, env: true, it: true */ describe("jsdoc/util/templateHelper", function() { var helper = require('jsdoc/util/templateHelper'); helper.registerLink('test', 'path/to/test.html'); @@ -137,5 +138,82 @@ describe("jsdoc/util/templateHelper", function() { }); }); - //TODO: tests for tutorial functions? + xdescribe("registerLink", function() { + // TODO + }); + + xdescribe("longnameToUrl", function() { + // TODO + }); + + xdescribe("setTutorials", function() { + // TODO + }); + + describe("toTutorial", function() { + /*jshint evil: true */ + + // TODO: more tests + + var lenient = !!env.opts.lenient, + log = eval(console.log); + + function missingParam() { + helper.toTutorial(); + } + + afterEach(function() { + env.opts.lenient = lenient; + console.log = log; + }); + + it('throws an exception if the first param is missing and the lenient option is not enabled', function() { + env.opts.lenient = false; + + expect(missingParam).toThrow(); + }); + + it('does not throw an exception if the first param is missing and the lenient option is enabled', function() { + console.log = function() {}; + env.opts.lenient = true; + + expect(missingParam).not.toThrow(); + }); + }); + + describe("tutorialToUrl", function() { + /*jshint evil: true */ + + // TODO: more tests + + var lenient = !!env.opts.lenient, + log = eval(console.log); + + function missingTutorial() { + var url = helper.tutorialToUrl("be-a-perfect-person-in-just-three-days"); + } + + beforeEach(function() { + var root = require('jsdoc/tutorial/resolver').root; + helper.setTutorials(root); + }); + + afterEach(function() { + helper.setTutorials(null); + env.opts.lenient = lenient; + console.log = log; + }); + + it('throws an exception if the tutorial is missing and the lenient option is not enabled', function() { + env.opts.lenient = false; + expect(missingTutorial).toThrow(); + }); + + it('does not throw an exception if the tutorial is missing and the lenient option is enabled', function() { + console.log = function() {}; + env.opts.lenient = true; + + expect(missingTutorial).not.toThrow(); + }); + }); }); diff --git a/test/tutorials/incomplete/test.html b/test/tutorials/incomplete/test.html new file mode 100644 index 00000000..aa4a4ff9 --- /dev/null +++ b/test/tutorials/incomplete/test.html @@ -0,0 +1,3 @@ +
{@link Test}
diff --git a/test/tutorials/incomplete/test.js b/test/tutorials/incomplete/test.js new file mode 100644 index 00000000..078dc3f5 --- /dev/null +++ b/test/tutorials/incomplete/test.js @@ -0,0 +1 @@ +{"title": "missing child tutorial", "children": ["test2"]}