it("logs an error for tutorials defined twice in .json files", function() { // can't have a tutorial's metadata defined twice in .json files resolver.load(env.dirname + "/test/tutorials/duplicateDefined"); resolver.resolve(); expect(logger.error).toHaveBeenCalled(); });
JSDoc.resolveTutorials = function() { var resolver = require('jsdoc/tutorial/resolver'); if (env.opts.tutorials) { resolver.load(env.opts.tutorials); resolver.resolve(); } };
it('allows tutorials to be defined in one .json file and redefined in another', function() { resolver.load(global.env.dirname + '/test/tutorials/duplicateDefined'); resolver.resolve(); expect(logger.error).not.toHaveBeenCalled(); expect(logger.warn).toHaveBeenCalled(); // we don't check to see which one wins; it depends on the order in which the JS engine // iterates over object keys expect(resolver.root.getByName('asdf')).toBeDefined(); });
function loadTutorials() { resetRootTutorial(); resolver.load(global.env.dirname + '/test/tutorials/tutorials'); childNames = resolver.root.children.map(function (t) { return t.name; }); test = resolver.root.getByName('test'); test2 = resolver.root.getByName('test2'); test3 = resolver.root.getByName('test3'); test4 = resolver.root.getByName('test4'); test6 = resolver.root.getByName('test6'); constr = resolver.root.getByName('constructor'); }
function loadTutorials() { resetRootTutorial(); resolver.load(`${env.dirname}/test/tutorials/tutorials`); childNames = resolver.root.children.map(({name}) => name); test = resolver.root.getByName('test'); test2 = resolver.root.getByName('test2'); test3 = resolver.root.getByName('test3'); test4 = resolver.root.getByName('test4'); test6 = resolver.root.getByName('test6'); constr = resolver.root.getByName('constructor'); }
it("creates links to tutorials if they exist", function() { // NOTE: we have to set lenient = true here because otherwise JSDoc will // cry when trying to resolve the same set of tutorials twice (once // for the tutorials tests, and once here). env.opts.lenient = true; // load the tutorials we already have for the tutorials tests resolver.load(__dirname + "/test/tutorials/tutorials"); resolver.resolve(); var url = helper.tutorialToUrl('test'); expect(typeof url).toBe('string'); expect(url).toBe('tutorial-test.html'); });
it("returns a link to the tutorial if not missing", function() { // NOTE: we have to set lenient = true here because otherwise JSDoc will // cry when trying to resolve the same set of tutorials twice (once // for the tutorials tests, and once here). env.opts.lenient = true; spyOn(console, 'log'); // load the tutorials we already have for the tutorials tests resolver.load(__dirname + "/test/tutorials/tutorials"); resolver.resolve(); var link = helper.toTutorial('constructor', 'The Constructor tutorial'); expect(link).toBe('<a href="' + helper.tutorialToUrl('constructor') + '">The Constructor tutorial</a>'); });
it('logs an error for missing tutorials', function() { resolver.load(global.env.dirname + '/test/tutorials/incomplete'); resolver.resolve(); expect(logger.error).toHaveBeenCalled(); });
it("logs an error for missing tutorials", function() { resolver.load(env.dirname + "/test/tutorials/incomplete"); resolver.resolve(); expect(logger.error).toHaveBeenCalled(); });
describe("jsdoc/tutorial/resolver", function() { var logger = require('jsdoc/util/logger'); var resolver = require('jsdoc/tutorial/resolver'); var tutorial = require('jsdoc/tutorial'); it("should exist", function() { expect(resolver).toBeDefined(); expect(typeof resolver).toBe('object'); }); it("should export a 'addTutorial' function", function() { expect(resolver.addTutorial).toBeDefined(); expect(typeof resolver.addTutorial).toBe("function"); }); it("should export a 'load' function", function() { expect(resolver.load).toBeDefined(); expect(typeof resolver.load).toBe("function"); }); it("should export a 'resolve' function", function() { expect(resolver.resolve).toBeDefined(); expect(typeof resolver.resolve).toBe("function"); }); it("should export a 'root' tutorial", function() { expect(resolver.root).toBeDefined(); expect(resolver.root instanceof tutorial.Tutorial).toBe(true); }); it("exported 'root' tutorial should export a 'getByName' function", function() { expect(resolver.root.getByName).toBeDefined(); expect(typeof resolver.root.getByName).toBe("function"); }); // note: every time we addTutorial or run the resolver, we are *adding* // to the root tutorial. // addTutorial var tute = new tutorial.Tutorial('myTutorial', '', tutorial.TYPES.HTML); resolver.addTutorial(tute); describe("addTutorial", function() { it("should add a default parent of the root tutorial", function() { expect(tute.parent).toBe(resolver.root); }); it("should be added to the root tutorial as a child", function() { expect(resolver.root.children).toContain(tute); }); }); // root.getByName describe("root.getByName", function() { it("can retrieve tutorials by name", function() { expect(resolver.root.getByName('myTutorial')).toBe(tute); }); it("returns nothing for non-existent tutorials", function() { expect(resolver.root.getByName('asdf')).toBeFalsy(); }); it("is careful with tutorials whose names are reserved keywords in JS", function() { expect(resolver.root.getByName('prototype')).toBeFalsy(); }); }); // load resolver.load(env.dirname + "/test/tutorials/tutorials"); var childNames = resolver.root.children.map(function (t) { return t.name; }), test = resolver.root.getByName('test'), test2 = resolver.root.getByName('test2'), test3 = resolver.root.getByName('test3'), test4 = resolver.root.getByName('test4'), test6 = resolver.root.getByName('test6'), constr = resolver.root.getByName('constructor'); describe("load", function() { it("all tutorials are added, initially as top-level tutorials", function() { // check they were added expect(test).toBeDefined(); expect(test2).toBeDefined(); expect(test3).toBeDefined(); expect(test4).toBeDefined(); expect(test6).toBeDefined(); expect(constr).toBeDefined(); // check they are top-level in resolver.root expect(childNames).toContain('test'); expect(childNames).toContain('test2'); expect(childNames).toContain('test3'); expect(childNames).toContain('test4'); expect(childNames).toContain('test6'); }); it("tutorials with names equal to reserved keywords in JS still function as expected", function() { expect(constr instanceof tutorial.Tutorial).toBe(true); }); it("non-tutorials are skipped", function() { expect(resolver.root.getByName('multiple')).toBeFalsy(); expect(resolver.root.getByName('test5')).toBeFalsy(); }); it("tutorial types are determined correctly", function() { // test.html, test2.markdown, test3.html, test4.md, test6.xml expect(test.type).toBe(tutorial.TYPES.HTML); expect(test2.type).toBe(tutorial.TYPES.MARKDOWN); expect(test3.type).toBe(tutorial.TYPES.HTML); expect(test4.type).toBe(tutorial.TYPES.MARKDOWN); expect(test6.type).toBe(tutorial.TYPES.HTML); expect(constr.type).toBe(tutorial.TYPES.MARKDOWN); }); }); // resolve // myTutorial // constructor // test // |- test2 // |- test6 // |- test3 // |- test4 describe("resolve", function() { resolver.resolve(); it("hierarchy is resolved properly no matter how the children property is defined", function() { // root has child 'test' expect(resolver.root.children.length).toBe(3); expect(resolver.root.children).toContain(test); expect(resolver.root.children).toContain(constr); expect(test.parent).toBe(resolver.root); expect(constr.parent).toBe(resolver.root); // test has child 'test2' expect(test.children.length).toBe(1); expect(test.children).toContain(test2); expect(test2.parent).toBe(test); // test2 has children test3, test6 expect(test2.children.length).toBe(2); expect(test2.children).toContain(test3); expect(test2.children).toContain(test6); expect(test3.parent).toBe(test2); expect(test6.parent).toBe(test2); // test3 has child test4 expect(test3.children.length).toBe(1); expect(test3.children).toContain(test4); expect(test4.parent).toBe(test3); }); it("tutorials without configuration files have titles matching filenames", function() { // test6.xml didn't have a metadata expect(test6.title).toBe('test6'); }); it("tutorials with configuration files have titles as specified in configuration", function() { // test.json had info for just test.json expect(test.title).toBe("Test tutorial"); }); it("multiple tutorials can appear in a configuration file", function() { expect(test2.title).toBe("Test 2"); expect(test3.title).toBe("Test 3"); expect(test4.title).toBe("Test 4"); }); }); // error reporting. describe("Error reporting", function() { beforeEach(function() { spyOn(logger, 'error'); spyOn(logger, 'warn'); }); it("logs an error for missing tutorials", function() { resolver.load(env.dirname + "/test/tutorials/incomplete"); resolver.resolve(); expect(logger.error).toHaveBeenCalled(); }); it("logs a warning for duplicate-named tutorials (e.g. test.md, test.html)", function() { resolver.addTutorial(tute); expect(logger.warn).toHaveBeenCalled(); }); it("logs an error for tutorials defined twice in .json files", function() { // can't have a tutorial's metadata defined twice in .json files resolver.load(env.dirname + "/test/tutorials/duplicateDefined"); resolver.resolve(); expect(logger.error).toHaveBeenCalled(); }); }); });
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// /** * Run the jsdoc application. * @todo Refactor function (and require statements) into smaller functions */ function main() { var _ = require('underscore'); var args = require('jsdoc/opts/args'); var augment = require('jsdoc/augment'); var borrow = require('jsdoc/borrow'); var Config = require('jsdoc/config'); var Filter = require('jsdoc/src/filter').Filter; var fs = require('jsdoc/fs'); var handlers = require('jsdoc/src/handlers'); var include = require('jsdoc/util/include'); var Package = require('jsdoc/package').Package; var path = require('jsdoc/path'); var plugins = require('jsdoc/plugins'); var Readme = require('jsdoc/readme'); var resolver = require('jsdoc/tutorial/resolver'); var taffy = require('taffydb').taffy; var vm = require('jsdoc/util/vm'); var defaultOpts; var docs; var exampleConf; var filter; var i; var info; var l; var packageDocs; var packageJson; var sourceFiles; var template; defaultOpts = { destination: './out/', encoding: 'utf8' }; // get JSDoc version number info = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8')); env.version = { number: info.version, revision: new Date(parseInt(info.revision, 10)).toUTCString() }; env.opts = args.parse(env.args); try { env.conf = new Config( fs.readFileSync( env.opts.configure || path.join(__dirname, 'conf.json'), 'utf8' ) ).get(); } catch (e) { try { // Use the example file if possible exampleConf = fs.readFileSync(path.join(__dirname, 'conf.json.EXAMPLE'), 'utf8'); env.conf = JSON.parse(exampleConf); } catch(e) { throw('Configuration file cannot be evaluated. ' + e); } } // look for options on the command line, in the config file, and in the defaults, in that order env.opts = _.defaults(env.opts, env.conf.opts, defaultOpts); // which version of javascript will be supported? (rhino only) if (typeof version === 'function') { version(env.conf.jsVersion || 180); } if (env.opts.help) { console.log( args.help() ); process.exit(0); } else if (env.opts.test) { include('test/runner.js'); process.exit(0); } else if (env.opts.version) { console.log('JSDoc ' + env.version.number + ' (' + env.version.revision + ')'); process.exit(0); } if (env.conf.plugins) { plugins.installPlugins(env.conf.plugins, app.jsdoc.parser); } if (env.conf.source && env.conf.source.include) { env.opts._ = (env.opts._ || []).concat(env.conf.source.include); } // any source file named package.json or README.md is treated special for (i = 0, l = env.opts._.length; i < l; i++ ) { if (/\bpackage\.json$/i.test(env.opts._[i])) { packageJson = fs.readFileSync( env.opts._[i], 'utf8' ); env.opts._.splice(i--, 1); } if (/(\bREADME|\.md)$/i.test(env.opts._[i])) { env.opts.readme = new Readme(env.opts._[i]).html; env.opts._.splice(i--, 1); } } if (env.conf.source && env.opts._.length > 0) { // are there any files to scan and parse? filter = new Filter(env.conf.source); sourceFiles = app.jsdoc.scanner.scan(env.opts._, (env.opts.recurse? 10 : undefined), filter); 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 packageDocs = new Package(packageJson); packageDocs.files = sourceFiles || []; docs.push(packageDocs); borrow.indexAll(docs); augment.addInherited(docs); borrow.resolveBorrows(docs); if (env.opts.explain) { console.log(docs); process.exit(0); } if (env.opts.tutorials) { resolver.load(env.opts.tutorials); resolver.resolve(); } env.opts.template = (function() { var publish = env.opts.template || 'templates/default'; // if we don't find it, keep the user-specified value so the error message is useful return path.getResourcePath(publish) || env.opts.template; })(); try { template = require(env.opts.template + '/publish'); } catch(e) { throw new Error('Unable to load template: ' + e.message || e); } // templates should include a publish.js file that exports a "publish" function if (template.publish && typeof template.publish === 'function') { // convert this from a URI back to a path if necessary env.opts.template = path._uriToPath(env.opts.template); template.publish( taffy(docs), env.opts, resolver.root ); } else { // old templates define a global "publish" function, which is deprecated include(env.opts.template + '/publish.js'); if (publish && typeof publish === 'function') { console.log( env.opts.template + ' uses a global "publish" function, which is ' + 'deprecated and may not be supported in future versions. ' + 'Please update the template to use "exports.publish" instead.' ); // convert this from a URI back to a path if necessary env.opts.template = path._uriToPath(env.opts.template); publish( taffy(docs), env.opts, resolver.root ); } else { throw new Error( env.opts.template + ' does not export a "publish" function.' ); } } } }
function duplicateDefinedTutorials() { // can't have a tutorial's metadata defined twice in .json files resolver.load(__dirname + "/test/tutorials/duplicateDefined"); resolver.resolve(); }
// Tests for error reporting. function missingTutorial() { resolver.load(__dirname + "/test/tutorials/incomplete"); resolver.resolve(); }
describe("jsdoc/tutorial/resolver", function() { var resolver = require('jsdoc/tutorial/resolver'), tutorial = require('jsdoc/tutorial'), lenient = !!env.opts.lenient, log = eval(console.log); /*jshint evil: true */ it("should exist", function() { expect(resolver).toBeDefined(); expect(typeof resolver).toEqual('object'); }); it("should export a 'addTutorial' function", function() { expect(resolver.addTutorial).toBeDefined(); expect(typeof resolver.addTutorial).toEqual("function"); }); it("should export a 'load' function", function() { expect(resolver.load).toBeDefined(); expect(typeof resolver.load).toEqual("function"); }); it("should export a 'resolve' function", function() { expect(resolver.resolve).toBeDefined(); expect(typeof resolver.resolve).toEqual("function"); }); it("should export a 'root' tutorial", function() { expect(resolver.root).toBeDefined(); expect(resolver.root instanceof tutorial.Tutorial).toEqual(true); }); it("exported 'root' tutorial should export a 'getByName' function", function() { expect(resolver.root.getByName).toBeDefined(); expect(typeof resolver.root.getByName).toEqual("function"); }); // note: every time we addTutorial or run the resolver, we are *adding* // to the root tutorial. // addTutorial var tute = new tutorial.Tutorial('myTutorial', '', tutorial.TYPES.HTML); resolver.addTutorial(tute); describe("addTutorial", function() { it("should add a default parent of the root tutorial", function() { expect(tute.parent).toEqual(resolver.root); }); it("should be added to the root tutorial as a child", function() { expect(resolver.root.children[0]).toEqual(tute); }); }); // root.getByName describe("root.getByName", function() { it("can retrieve tutorials by name", function() { expect(resolver.root.getByName('myTutorial')).toEqual(tute); }); }); // load resolver.load(__dirname + "/test/tutorials/tutorials"); var childNames = resolver.root.children.map(function (t) { return t.name; }), test = resolver.root.getByName('test'), test2 = resolver.root.getByName('test2'), test3 = resolver.root.getByName('test3'), test4 = resolver.root.getByName('test4'); test6 = resolver.root.getByName('test6'); describe("load", function() { it("all tutorials are added, initially as top-level tutorials", function() { // check they were added expect(test).toBeDefined(); expect(test2).toBeDefined(); expect(test3).toBeDefined(); expect(test4).toBeDefined(); expect(test6).toBeDefined(); // check they are top-level in resolver.root expect(childNames.indexOf('test')).not.toEqual(-1); expect(childNames.indexOf('test2')).not.toEqual(-1); expect(childNames.indexOf('test3')).not.toEqual(-1); expect(childNames.indexOf('test4')).not.toEqual(-1); expect(childNames.indexOf('test6')).not.toEqual(-1); }); it("non-tutorials are skipped", function() { expect(resolver.root.getByName('multple')).toBeUndefined(); expect(resolver.root.getByName('test5')).toBeUndefined(); }); it("tutorial types are determined correctly", function() { // test.html, test2.markdown, test3.html, test4.md, test6.xml expect(test.type).toEqual(tutorial.TYPES.HTML); expect(test2.type).toEqual(tutorial.TYPES.MARKDOWN); expect(test3.type).toEqual(tutorial.TYPES.HTML); expect(test4.type).toEqual(tutorial.TYPES.MARKDOWN); expect(test6.type).toEqual(tutorial.TYPES.HTML); }); }); // resolve // myTutorial // test // |- test2 // |- test6 // |- test3 // |- test4 describe("resolve", function() { resolver.resolve(); it("hierarchy is resolved properly no matter how the children property is defined", function() { // root has child 'test' expect(resolver.root.children.length).toEqual(2); expect(resolver.root.children.indexOf(test)).not.toEqual(-1); expect(test.parent).toEqual(resolver.root); // test has child 'test2' expect(test.children.length).toEqual(1); expect(test.children[0]).toEqual(test2); expect(test2.parent).toEqual(test); // test2 has children test3, test6 expect(test2.children.length).toEqual(2); expect(test2.children.indexOf(test3)).not.toEqual(-1); expect(test2.children.indexOf(test6)).not.toEqual(-1); expect(test3.parent).toEqual(test2); expect(test6.parent).toEqual(test2); // test3 has child test4 expect(test3.children.length).toEqual(1); expect(test3.children[0]).toEqual(test4); expect(test4.parent).toEqual(test3); }); it("tutorials without configuration files have titles matching filenames", function() { // test6.xml didn't have a metadata expect(test6.title).toEqual('test6'); }); it("tutorials with configuration files have titles matching filenames", function() { // test.json had info for just test.json expect(test.title).toEqual("Test tutorial"); }); it("multiple tutorials can appear in a configuration file", function() { expect(test2.title).toEqual("Test 2"); expect(test3.title).toEqual("Test 3"); expect(test4.title).toEqual("Test 4"); }); }); // error reporting. describe("Error reporting", function() { // Tests for error reporting. function missingTutorial() { resolver.load(__dirname + "/test/tutorials/incomplete"); resolver.resolve(); } function duplicateNamedTutorials() { // can't add a tutorial if another with its name has already been added resolver.addTutorial(tute); } function duplicateDefinedTutorials() { // can't have a tutorial's metadata defined twice in .json files resolver.load(__dirname + "/test/tutorials/duplicateDefined"); 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(); }); it("throws an exception for duplicate-named tutorials (e.g. test.md, test.html) if the lenient option is not enabled", function() { env.opts.lenient = false; expect(duplicateNamedTutorials).toThrow(); }); it("doesn't throw an exception for duplicate-named tutorials (e.g. test.md, test.html) if the lenient option is not enabled", function() { console.log = function() {}; env.opts.lenient = true; expect(duplicateNamedTutorials).not.toThrow(); }); it("throws an exception for tutorials defined twice in .jsons if the lenient option is not enabled", function() { env.opts.lenient = false; expect(duplicateDefinedTutorials).toThrow(); }); it("doesn't throw an exception for tutorials defined twice in .jsons if the lenient option is not enabled", function() { console.log = function() {}; env.opts.lenient = true; expect(duplicateDefinedTutorials).not.toThrow(); }); }); });
it('logs an error for missing tutorials', () => { resolver.load(`${env.dirname}/test/tutorials/incomplete`); resolver.resolve(); expect(logger.error).toHaveBeenCalled(); });
function loadBomTutorials() { resolver.load(`${env.dirname}/test/tutorials/bom`); }