mirror of
https://github.com/jsdoc/jsdoc.git
synced 2025-12-08 19:46:11 +00:00
feature(jsdoc): temporarily remove tutorial support
To be replaced by more flexible support for additional content. BREAKING CHANGE: Removed support for tutorials.
This commit is contained in:
parent
3d7e3307a6
commit
65e2226eb0
@ -96,12 +96,6 @@ module.exports = {
|
||||
boolean: true,
|
||||
description: 'Run all tests and exit.'
|
||||
},
|
||||
tutorials: {
|
||||
alias: 'u',
|
||||
description: 'The directory to search for tutorials.',
|
||||
normalize: true,
|
||||
requiresArg: true
|
||||
},
|
||||
verbose: {
|
||||
boolean: true,
|
||||
description: 'Log detailed information to the console.'
|
||||
|
||||
@ -343,8 +343,6 @@ module.exports = (() => {
|
||||
return Promise.resolve();
|
||||
}
|
||||
else {
|
||||
cli.resolveTutorials();
|
||||
|
||||
return cli.generateDocs();
|
||||
}
|
||||
};
|
||||
@ -355,21 +353,9 @@ module.exports = (() => {
|
||||
return cli;
|
||||
};
|
||||
|
||||
cli.resolveTutorials = () => {
|
||||
const resolver = require('jsdoc/tutorial/resolver');
|
||||
|
||||
if (env.opts.tutorials) {
|
||||
resolver.load(env.opts.tutorials);
|
||||
resolver.resolve();
|
||||
}
|
||||
|
||||
return cli;
|
||||
};
|
||||
|
||||
cli.generateDocs = () => {
|
||||
let message;
|
||||
const path = require('path');
|
||||
const resolver = require('jsdoc/tutorial/resolver');
|
||||
const taffy = require('taffydb').taffy;
|
||||
|
||||
let template;
|
||||
@ -392,8 +378,7 @@ module.exports = (() => {
|
||||
log.info('Generating output files...');
|
||||
publishPromise = template.publish(
|
||||
taffy(props.docs),
|
||||
env.opts,
|
||||
resolver.root
|
||||
env.opts
|
||||
);
|
||||
|
||||
return Promise.resolve(publishPromise);
|
||||
|
||||
@ -491,14 +491,6 @@ const DOCLET_SCHEMA = exports.DOCLET_SCHEMA = {
|
||||
type: STRING
|
||||
}
|
||||
},
|
||||
// extended tutorials
|
||||
tutorials: {
|
||||
type: ARRAY,
|
||||
minItems: 1,
|
||||
items: {
|
||||
type: STRING
|
||||
}
|
||||
},
|
||||
// what type is the value that this doc is associated with, like `number`
|
||||
type: TYPE_PROPERTY_SCHEMA,
|
||||
undocumented: {
|
||||
|
||||
@ -760,13 +760,6 @@ let baseTags = exports.baseTags = {
|
||||
},
|
||||
synonyms: ['exception']
|
||||
},
|
||||
tutorial: {
|
||||
mustHaveValue: true,
|
||||
onTagged(doclet, {value}) {
|
||||
doclet.tutorials = doclet.tutorials || [];
|
||||
doclet.tutorials.push(value);
|
||||
}
|
||||
},
|
||||
type: {
|
||||
mustHaveValue: true,
|
||||
mustNotHaveDescription: true,
|
||||
|
||||
@ -1,153 +0,0 @@
|
||||
/**
|
||||
* @module jsdoc/tutorial
|
||||
*/
|
||||
const markdown = require('jsdoc/util/markdown');
|
||||
|
||||
const hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
|
||||
/**
|
||||
* Removes child tutorial from the parent. Does *not* unset child.parent though.
|
||||
*
|
||||
* @param {Tutorial} parent - parent tutorial.
|
||||
* @param {Tutorial} child - Old child.
|
||||
* @private
|
||||
*/
|
||||
function removeChild({children}, child) {
|
||||
const index = children.indexOf(child);
|
||||
|
||||
if (index !== -1) {
|
||||
children.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a child to the parent tutorial. Does *not* set child.parent though.
|
||||
*
|
||||
* @param {Tutorial} parent - parent tutorial.
|
||||
* @param {Tutorial} child - New child.
|
||||
* @private
|
||||
*/
|
||||
function addChild({children}, child) {
|
||||
children.push(child);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a single JSDoc tutorial.
|
||||
*/
|
||||
class Tutorial {
|
||||
/**
|
||||
* @param {string} name - Tutorial name.
|
||||
* @param {string} content - Text content.
|
||||
* @param {number} type - Source formating.
|
||||
|
||||
*/
|
||||
constructor(name, content, type) {
|
||||
this.title = this.name = this.longname = name;
|
||||
this.content = content;
|
||||
this.type = type;
|
||||
|
||||
// default values
|
||||
this.parent = null;
|
||||
this.children = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves children from current parent to different one.
|
||||
*
|
||||
* @param {?Tutorial} parent - New parent. If null, the tutorial has no parent.
|
||||
*/
|
||||
setParent(parent) {
|
||||
// removes node from old parent
|
||||
if (this.parent) {
|
||||
removeChild(this.parent, this);
|
||||
}
|
||||
|
||||
this.parent = parent;
|
||||
if (parent) {
|
||||
addChild(parent, this);
|
||||
}
|
||||
}
|
||||
|
||||
/* eslint-disable class-methods-use-this */
|
||||
/**
|
||||
* Removes children from current node.
|
||||
*
|
||||
* @param {Tutorial} child - Old child.
|
||||
*/
|
||||
removeChild(child) {
|
||||
child.setParent(null);
|
||||
}
|
||||
/* eslint-enable class-methods-use-this */
|
||||
|
||||
/**
|
||||
* Adds new children to current node.
|
||||
*
|
||||
* @param {Tutorial} child - New child.
|
||||
*/
|
||||
addChild(child) {
|
||||
child.setParent(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares source.
|
||||
*
|
||||
* @return {string} HTML source.
|
||||
*/
|
||||
parse() {
|
||||
switch (this.type) {
|
||||
// nothing to do
|
||||
case exports.TYPES.HTML:
|
||||
return this.content;
|
||||
|
||||
// markdown
|
||||
case exports.TYPES.MARKDOWN:
|
||||
return markdown.getParser()(this.content);
|
||||
|
||||
// uhm... should we react somehow?
|
||||
// if not then this case can be merged with TYPES.HTML
|
||||
default:
|
||||
return this.content;
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.Tutorial = Tutorial;
|
||||
|
||||
/**
|
||||
* Represents the root tutorial.
|
||||
* @extends {module:jsdoc/tutorial.Tutorial}
|
||||
*/
|
||||
class RootTutorial extends Tutorial {
|
||||
constructor() {
|
||||
super('', '', null);
|
||||
|
||||
this._tutorials = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a tutorial by name.
|
||||
* @param {string} name - Tutorial name.
|
||||
* @return {module:jsdoc/tutorial.Tutorial} Tutorial instance.
|
||||
*/
|
||||
getByName(name) {
|
||||
return hasOwnProp.call(this._tutorials, name) && this._tutorials[name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a child tutorial to the root.
|
||||
* @param {module:jsdoc/tutorial.Tutorial} child - Child tutorial.
|
||||
*/
|
||||
_addTutorial(child) {
|
||||
this._tutorials[child.name] = child;
|
||||
}
|
||||
}
|
||||
exports.RootTutorial = RootTutorial;
|
||||
|
||||
/**
|
||||
* Tutorial source types.
|
||||
*
|
||||
* @enum {number}
|
||||
*/
|
||||
exports.TYPES = {
|
||||
HTML: 1,
|
||||
MARKDOWN: 2
|
||||
};
|
||||
@ -1,192 +0,0 @@
|
||||
/**
|
||||
* @module jsdoc/tutorial/resolver
|
||||
*/
|
||||
const env = require('jsdoc/env');
|
||||
const fs = require('fs');
|
||||
const { log } = require('@jsdoc/util');
|
||||
const { lsSync } = require('@jsdoc/util').fs;
|
||||
const path = require('path');
|
||||
const stripBom = require('strip-bom');
|
||||
const tutorial = require('jsdoc/tutorial');
|
||||
|
||||
const hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
|
||||
// TODO: make this an instance member of `RootTutorial`?
|
||||
const conf = {};
|
||||
const finder = /^(.*)\.(x(?:ht)?ml|html?|md|markdown|json)$/i;
|
||||
|
||||
/** checks if `conf` is the metadata for a single tutorial.
|
||||
* A tutorial's metadata has a property 'title' and/or a property 'children'.
|
||||
* @param {object} json - the object we want to test (typically from JSON.parse)
|
||||
* @returns {boolean} whether `json` could be the metadata for a tutorial.
|
||||
*/
|
||||
function isTutorialJSON(json) {
|
||||
// if conf.title exists or conf.children exists, it is metadata for a tutorial
|
||||
return (hasOwnProp.call(json, 'title') || hasOwnProp.call(json, 'children'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Root tutorial.
|
||||
* @type {module:jsdoc/tutorial.Root}
|
||||
*/
|
||||
exports.root = new tutorial.RootTutorial();
|
||||
|
||||
/**
|
||||
* Helper function that adds tutorial configuration to the `conf` variable. This helps when multiple
|
||||
* tutorial configurations are specified in one object, or when a tutorial's children are specified
|
||||
* as tutorial configurations as opposed to an array of tutorial names.
|
||||
*
|
||||
* Recurses as necessary to ensure all tutorials are added.
|
||||
*
|
||||
* @param {string} name - if `meta` is a configuration for a single tutorial, this is that
|
||||
* tutorial's name.
|
||||
* @param {object} meta - object that contains tutorial information. Can either be for a single
|
||||
* tutorial, or for multiple (where each key in `meta` is the tutorial name and each value is the
|
||||
* information for a single tutorial). Additionally, a tutorial's 'children' property may either be
|
||||
* an array of strings (names of the child tutorials), OR an object giving the configuration for the
|
||||
* child tutorials.
|
||||
*/
|
||||
function addTutorialConf(name, meta) {
|
||||
let names;
|
||||
|
||||
if (isTutorialJSON(meta)) {
|
||||
// if the children are themselves tutorial defintions as opposed to an
|
||||
// array of strings, add each child.
|
||||
if (hasOwnProp.call(meta, 'children') && !Array.isArray(meta.children)) {
|
||||
names = Object.keys(meta.children);
|
||||
for (let childName of names) {
|
||||
addTutorialConf(childName, meta.children[childName]);
|
||||
}
|
||||
// replace with an array of names.
|
||||
meta.children = names;
|
||||
}
|
||||
// check if the tutorial has already been defined...
|
||||
if (hasOwnProp.call(conf, name)) {
|
||||
log.warn(
|
||||
`Metadata for the tutorial ${name} is defined more than once. ` +
|
||||
'Only the first definition will be used.'
|
||||
);
|
||||
} else {
|
||||
conf[name] = meta;
|
||||
}
|
||||
} else {
|
||||
// keys are tutorial names, values are `Tutorial` instances
|
||||
names = Object.keys(meta);
|
||||
for (let tutorialName of names) {
|
||||
addTutorialConf(tutorialName, meta[tutorialName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a tutorial.
|
||||
* @param {module:jsdoc/tutorial.Tutorial} current - Tutorial to add.
|
||||
*/
|
||||
exports.addTutorial = current => {
|
||||
if (exports.root.getByName(current.name)) {
|
||||
log.warn(
|
||||
`The tutorial ${current.name} is defined more than once. ` +
|
||||
'Only the first definition will be used.'
|
||||
);
|
||||
} else {
|
||||
// by default, the root tutorial is the parent
|
||||
current.setParent(exports.root);
|
||||
|
||||
exports.root._addTutorial(current);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Load tutorials from the given path.
|
||||
* @param {string} filepath - Tutorials directory.
|
||||
*/
|
||||
exports.load = filepath => {
|
||||
let content;
|
||||
let current;
|
||||
const files = lsSync(filepath, {
|
||||
depth: env.opts.recurse ? env.conf.recurseDepth : 0
|
||||
});
|
||||
let name;
|
||||
let match;
|
||||
let type;
|
||||
|
||||
// tutorials handling
|
||||
files.forEach(file => {
|
||||
match = file.match(finder);
|
||||
|
||||
// any filetype that can apply to tutorials
|
||||
if (match) {
|
||||
name = path.basename(match[1]);
|
||||
content = fs.readFileSync(file, env.opts.encoding);
|
||||
|
||||
switch (match[2].toLowerCase()) {
|
||||
// HTML type
|
||||
case 'xml':
|
||||
case 'xhtml':
|
||||
case 'html':
|
||||
case 'htm':
|
||||
type = tutorial.TYPES.HTML;
|
||||
break;
|
||||
|
||||
// Markdown typs
|
||||
case 'md':
|
||||
case 'markdown':
|
||||
type = tutorial.TYPES.MARKDOWN;
|
||||
break;
|
||||
|
||||
// configuration file
|
||||
case 'json':
|
||||
addTutorialConf(name, JSON.parse(stripBom(content)));
|
||||
|
||||
// don't add this as a tutorial
|
||||
return;
|
||||
|
||||
// how can it be? check `finder' regexp
|
||||
// not a file we want to work with
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
current = new tutorial.Tutorial(name, content, type);
|
||||
exports.addTutorial(current);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolves hierarchical structure.
|
||||
*/
|
||||
exports.resolve = () => {
|
||||
let item;
|
||||
let current;
|
||||
|
||||
Object.keys(conf).forEach(name => {
|
||||
current = exports.root.getByName(name);
|
||||
|
||||
// TODO: should we complain about this?
|
||||
if (!current) {
|
||||
return;
|
||||
}
|
||||
|
||||
item = conf[name];
|
||||
|
||||
// set title
|
||||
if (item.title) {
|
||||
current.title = item.title;
|
||||
}
|
||||
|
||||
// add children
|
||||
if (item.children) {
|
||||
item.children.forEach(child => {
|
||||
const childTutorial = exports.root.getByName(child);
|
||||
|
||||
if (!childTutorial) {
|
||||
log.error(`Missing child tutorial: ${child}`);
|
||||
}
|
||||
else {
|
||||
childTutorial.setParent(current);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -18,15 +18,6 @@ const ids = {};
|
||||
// each container gets its own html file
|
||||
const containers = ['class', 'module', 'external', 'namespace', 'mixin', 'interface'];
|
||||
|
||||
let tutorials;
|
||||
|
||||
/** Sets tutorials map.
|
||||
@param {jsdoc.tutorial.Tutorial} root - Root tutorial node.
|
||||
*/
|
||||
exports.setTutorials = root => {
|
||||
tutorials = root;
|
||||
};
|
||||
|
||||
exports.globalName = SCOPE.NAMES.GLOBAL;
|
||||
exports.fileExtension = '.html';
|
||||
exports.SCOPE_TO_PUNC = SCOPE_TO_PUNC;
|
||||
@ -40,12 +31,6 @@ const linkMap = {
|
||||
longnameToId: {}
|
||||
};
|
||||
|
||||
// two-way lookup
|
||||
const tutorialLinkMap = {
|
||||
nameToUrl: {},
|
||||
urlToName: {}
|
||||
};
|
||||
|
||||
const longnameToUrl = exports.longnameToUrl = linkMap.longnameToUrl;
|
||||
const longnameToId = exports.longnameToId = linkMap.longnameToId;
|
||||
|
||||
@ -429,80 +414,6 @@ function splitLinkText(text) {
|
||||
};
|
||||
}
|
||||
|
||||
const tutorialToUrl = exports.tutorialToUrl = tutorial => {
|
||||
let fileUrl;
|
||||
const node = tutorials.getByName(tutorial);
|
||||
|
||||
// no such tutorial
|
||||
if (!node) {
|
||||
log.error( new Error(`No such tutorial: ${tutorial}`) );
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// define the URL if necessary
|
||||
if (!hasOwnProp.call(tutorialLinkMap.nameToUrl, node.name)) {
|
||||
fileUrl = `tutorial-${getUniqueFilename(node.name)}`;
|
||||
tutorialLinkMap.nameToUrl[node.name] = fileUrl;
|
||||
tutorialLinkMap.urlToName[fileUrl] = node.name;
|
||||
}
|
||||
|
||||
return tutorialLinkMap.nameToUrl[node.name];
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve a link to a tutorial, or the name of the tutorial if the tutorial is missing. If the
|
||||
* `missingOpts` parameter is supplied, the names of missing tutorials will be prefixed by the
|
||||
* specified text and wrapped in the specified HTML tag and CSS class.
|
||||
*
|
||||
* @function
|
||||
* @todo Deprecate missingOpts once we have a better error-reporting mechanism.
|
||||
* @param {string} tutorial The name of the tutorial.
|
||||
* @param {string} content The link text to use.
|
||||
* @param {object} [missingOpts] Options for displaying the name of a missing tutorial.
|
||||
* @param {string} missingOpts.classname The CSS class to wrap around the tutorial name.
|
||||
* @param {string} missingOpts.prefix The prefix to add to the tutorial name.
|
||||
* @param {string} missingOpts.tag The tag to wrap around the tutorial name.
|
||||
* @return {string} An HTML link to the tutorial, or the name of the tutorial with the specified
|
||||
* options.
|
||||
*/
|
||||
const toTutorial = exports.toTutorial = (tutorial, content, missingOpts) => {
|
||||
let classname;
|
||||
let link;
|
||||
let node;
|
||||
let tag;
|
||||
|
||||
|
||||
if (!tutorial) {
|
||||
log.error(new Error('Missing required parameter: tutorial'));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
node = tutorials.getByName(tutorial);
|
||||
// no such tutorial
|
||||
if (!node) {
|
||||
missingOpts = missingOpts || {};
|
||||
tag = missingOpts.tag;
|
||||
classname = missingOpts.classname;
|
||||
|
||||
link = tutorial;
|
||||
if (missingOpts.prefix) {
|
||||
link = missingOpts.prefix + link;
|
||||
}
|
||||
if (tag) {
|
||||
link = `<${tag}${classname ? (` class="${classname}">`) : '>'}${link}`;
|
||||
link += `</${tag}>`;
|
||||
}
|
||||
|
||||
return link;
|
||||
}
|
||||
|
||||
content = content || node.title;
|
||||
|
||||
return `<a href="${tutorialToUrl(tutorial)}">${content}</a>`;
|
||||
};
|
||||
|
||||
function shouldShortenLongname() {
|
||||
if (env.conf && env.conf.templates && env.conf.templates.useShortNamesInLinks) {
|
||||
return true;
|
||||
@ -512,9 +423,9 @@ function shouldShortenLongname() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Find `{@link ...}` and `{@tutorial ...}` inline tags and turn them into HTML links.
|
||||
* Find `{@link ...}` inline tags and turn them into HTML links.
|
||||
*
|
||||
* @param {string} str - The string to search for `{@link ...}` and `{@tutorial ...}` tags.
|
||||
* @param {string} str - The string to search for `{@link ...}` tags.
|
||||
* @return {string} The linkified text.
|
||||
*/
|
||||
exports.resolveLinks = str => {
|
||||
@ -565,19 +476,10 @@ exports.resolveLinks = str => {
|
||||
}) );
|
||||
}
|
||||
|
||||
function processTutorial(string, {completeTag, text}) {
|
||||
const leading = extractLeadingText(string, completeTag);
|
||||
|
||||
string = leading.string;
|
||||
|
||||
return string.replace( completeTag, toTutorial(text, leading.leadingText) );
|
||||
}
|
||||
|
||||
replacers = {
|
||||
link: processLink,
|
||||
linkcode: processLink,
|
||||
linkplain: processLink,
|
||||
tutorial: processTutorial
|
||||
linkplain: processLink
|
||||
};
|
||||
|
||||
return inline.replaceInlineTags(str, replacers).newString;
|
||||
|
||||
@ -43,14 +43,6 @@ function find(spec) {
|
||||
return helper.find(data, spec);
|
||||
}
|
||||
|
||||
function tutoriallink(tutorial) {
|
||||
return helper.toTutorial(tutorial, null, {
|
||||
tag: 'em',
|
||||
classname: 'disabled',
|
||||
prefix: 'Tutorial: '
|
||||
});
|
||||
}
|
||||
|
||||
function getAncestorLinks(doclet) {
|
||||
return helper.getAncestorLinks(data, doclet);
|
||||
}
|
||||
@ -353,10 +345,6 @@ function buildMemberNav(items, itemHeading, itemsSeen, linktoFn) {
|
||||
return nav;
|
||||
}
|
||||
|
||||
function linktoTutorial(longName, name) {
|
||||
return tutoriallink(name);
|
||||
}
|
||||
|
||||
function linktoExternal(longName, name) {
|
||||
return linkto(longName, name.replace(/(^"|"$)/g, ''));
|
||||
}
|
||||
@ -370,7 +358,6 @@ function linktoExternal(longName, name) {
|
||||
* @param {array<object>} members.mixins
|
||||
* @param {array<object>} members.modules
|
||||
* @param {array<object>} members.namespaces
|
||||
* @param {array<object>} members.tutorials
|
||||
* @param {array<object>} members.events
|
||||
* @param {array<object>} members.interfaces
|
||||
* @return {string} The HTML for the navigation sidebar.
|
||||
@ -379,7 +366,6 @@ function buildNav(members) {
|
||||
let globalNav;
|
||||
let nav = '<h2><a href="index.html">Home</a></h2>';
|
||||
const seen = {};
|
||||
const seenTutorials = {};
|
||||
|
||||
nav += buildMemberNav(members.modules, 'Modules', {}, linkto);
|
||||
nav += buildMemberNav(members.externals, 'Externals', seen, linktoExternal);
|
||||
@ -388,7 +374,6 @@ function buildNav(members) {
|
||||
nav += buildMemberNav(members.interfaces, 'Interfaces', seen, linkto);
|
||||
nav += buildMemberNav(members.events, 'Events', seen, linkto);
|
||||
nav += buildMemberNav(members.mixins, 'Mixins', seen, linkto);
|
||||
nav += buildMemberNav(members.tutorials, 'Tutorials', seenTutorials, linktoTutorial);
|
||||
|
||||
if (members.globals.length) {
|
||||
globalNav = '';
|
||||
@ -421,9 +406,8 @@ function sourceToDestination(parentDir, sourcePath, destDir) {
|
||||
/**
|
||||
@param {TAFFY} taffyData See <http://taffydb.com/>.
|
||||
@param {object} opts
|
||||
@param {Tutorial} tutorials
|
||||
*/
|
||||
exports.publish = (taffyData, opts, tutorials) => {
|
||||
exports.publish = (taffyData, opts) => {
|
||||
let classes;
|
||||
let conf;
|
||||
let cwd;
|
||||
@ -469,9 +453,6 @@ exports.publish = (taffyData, opts, tutorials) => {
|
||||
path.resolve(conf.default.layoutFile) :
|
||||
'layout.tmpl';
|
||||
|
||||
// set up tutorials for helper
|
||||
helper.setTutorials(tutorials);
|
||||
|
||||
data = helper.prune(data);
|
||||
data.sort('longname, version, since');
|
||||
helper.addEventListeners(data);
|
||||
@ -656,7 +637,6 @@ exports.publish = (taffyData, opts, tutorials) => {
|
||||
});
|
||||
|
||||
members = helper.getMembers(data);
|
||||
members.tutorials = tutorials.children;
|
||||
|
||||
// output pretty-printed source files by default
|
||||
outputSourceFiles = conf.default && conf.default.outputSourceFiles !== false;
|
||||
@ -665,7 +645,6 @@ exports.publish = (taffyData, opts, tutorials) => {
|
||||
view.find = find;
|
||||
view.linkto = linkto;
|
||||
view.resolveAuthorLinks = resolveAuthorLinks;
|
||||
view.tutoriallink = tutoriallink;
|
||||
view.htmlsafe = htmlsafe;
|
||||
view.outputSourceFiles = outputSourceFiles;
|
||||
|
||||
@ -733,31 +712,4 @@ exports.publish = (taffyData, opts, tutorials) => {
|
||||
generate(`Interface: ${myInterfaces[0].name}`, myInterfaces, helper.longnameToUrl[longname]);
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: move the tutorial functions to templateHelper.js
|
||||
function generateTutorial(title, tutorial, filename) {
|
||||
const tutorialData = {
|
||||
title: title,
|
||||
header: tutorial.title,
|
||||
content: tutorial.parse(),
|
||||
children: tutorial.children
|
||||
};
|
||||
const tutorialPath = path.join(outdir, filename);
|
||||
let html = view.render('tutorial.tmpl', tutorialData);
|
||||
|
||||
// yes, you can use {@link} in tutorials too!
|
||||
html = helper.resolveLinks(html); // turn {@link foo} into <a href="foodoc.html">foo</a>
|
||||
|
||||
fs.writeFileSync(tutorialPath, html, 'utf8');
|
||||
}
|
||||
|
||||
// tutorials can have only one parent so there is no risk for loops
|
||||
function saveChildren({children}) {
|
||||
children.forEach(child => {
|
||||
generateTutorial(`Tutorial: ${child.title}`, child, helper.tutorialToUrl(child.name));
|
||||
saveChildren(child);
|
||||
});
|
||||
}
|
||||
|
||||
saveChildren(tutorials);
|
||||
};
|
||||
|
||||
@ -114,15 +114,6 @@ if (data.defaultvalue && (data.defaultvaluetype === 'object' || data.defaultvalu
|
||||
</li></ul></dd>
|
||||
<?js } ?>
|
||||
|
||||
<?js if (data.tutorials && tutorials.length) {?>
|
||||
<dt class="tag-tutorial">Tutorials:</dt>
|
||||
<dd class="tag-tutorial">
|
||||
<ul><?js tutorials.forEach(function(t) { ?>
|
||||
<li><?js= self.tutoriallink(t) ?></li>
|
||||
<?js }); ?></ul>
|
||||
</dd>
|
||||
<?js } ?>
|
||||
|
||||
<?js if (data.see && see.length) {?>
|
||||
<dt class="tag-see">See:</dt>
|
||||
<dd class="tag-see">
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
<section>
|
||||
|
||||
<header>
|
||||
<?js if (children.length > 0) { ?>
|
||||
<ul><?js
|
||||
var self = this;
|
||||
children.forEach(function(t) { ?>
|
||||
<li><?js= self.tutoriallink(t.name) ?></li>
|
||||
<?js }); ?></ul>
|
||||
<?js } ?>
|
||||
|
||||
<h2><?js= header ?></h2>
|
||||
</header>
|
||||
|
||||
<article>
|
||||
<?js= content ?>
|
||||
</article>
|
||||
|
||||
</section>
|
||||
@ -2,6 +2,5 @@
|
||||
/**
|
||||
@param {TAFFY} taffyData See <http://taffydb.com/>.
|
||||
@param {object} opts
|
||||
@param {Tutorial} tutorials
|
||||
*/
|
||||
exports.publish = (taffyData, opts, tutorials) => {};
|
||||
exports.publish = (taffyData, opts) => {};
|
||||
|
||||
@ -1 +0,0 @@
|
||||
{}
|
||||
@ -1 +0,0 @@
|
||||
<h1>tutorial ASDF</h1>
|
||||
@ -1 +0,0 @@
|
||||
{"title": "Conflicting title"}
|
||||
@ -1,5 +0,0 @@
|
||||
{
|
||||
"asdf": {
|
||||
"title": "Tutorial Asdf"
|
||||
}
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
<h1>Test.html</h1>
|
||||
|
||||
<p>{@link Test}</p>
|
||||
@ -1 +0,0 @@
|
||||
{"title": "missing child tutorial", "children": ["child"]}
|
||||
@ -1,7 +0,0 @@
|
||||
/**
|
||||
* Test {@tutorial test2} {@tutorial test3}
|
||||
*
|
||||
* @class
|
||||
* @tutorial test
|
||||
*/
|
||||
function Test() {}
|
||||
@ -1 +0,0 @@
|
||||
This tutorial has a tricksy name to make sure we are not loading Array.constructor or Object.constructor.
|
||||
@ -1,12 +0,0 @@
|
||||
{
|
||||
"test2": {
|
||||
"title": "Test 2",
|
||||
"children": ["test3", "test6"]
|
||||
},
|
||||
"test3": {
|
||||
"title": "Test 3",
|
||||
"children": {
|
||||
"test4": {"title": "Test 4"}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
# test_recursive.md
|
||||
@ -1,3 +0,0 @@
|
||||
<h1>Test.html</h1>
|
||||
|
||||
<p>{@link Test}</p>
|
||||
@ -1 +0,0 @@
|
||||
{"title": "Test tutorial", "children": ["test2"]}
|
||||
@ -1 +0,0 @@
|
||||
# test2.markdown
|
||||
@ -1,3 +0,0 @@
|
||||
<h1>Test3.html</h1>
|
||||
|
||||
<p>{@link Test}</p>
|
||||
@ -1 +0,0 @@
|
||||
# test4.md
|
||||
@ -1 +0,0 @@
|
||||
Should not be included as a tutorial.
|
||||
@ -1 +0,0 @@
|
||||
<h1>test 6 - has no metadata</h1>
|
||||
5
packages/jsdoc/test/fixtures/tutorialtag.js
vendored
5
packages/jsdoc/test/fixtures/tutorialtag.js
vendored
@ -1,5 +0,0 @@
|
||||
/** Some documentation.
|
||||
* @tutorial tute1
|
||||
* @tutorial tute2
|
||||
*/
|
||||
var x;
|
||||
@ -1,276 +0,0 @@
|
||||
describe('jsdoc/tutorial', () => {
|
||||
const env = require('jsdoc/env');
|
||||
const tutorial = require('jsdoc/tutorial');
|
||||
|
||||
const name = 'tuteID';
|
||||
const content = 'Tutorial content blah blah blah & <';
|
||||
const tute = new tutorial.Tutorial(name, content, tutorial.TYPES.NOTAVALUE);
|
||||
const par = new tutorial.Tutorial('parent',
|
||||
"# This is the parent tutorial's <em>content & stuff</em> A_B X_Y",
|
||||
tutorial.TYPES.MARKDOWN);
|
||||
const par2 = new tutorial.Tutorial('parent2', '<h2>This is the second parent tutorial</h2>',
|
||||
tutorial.TYPES.HTML);
|
||||
const markdownEntities = new tutorial.Tutorial('markdown-entities',
|
||||
'<pre>This Markdown tutorial contains HTML entities: & < ></pre>',
|
||||
tutorial.TYPES.MARKDOWN);
|
||||
|
||||
it('module should exist', () => {
|
||||
expect(tutorial).toBeObject();
|
||||
});
|
||||
|
||||
it('should export a Tutorial function', () => {
|
||||
expect(tutorial.Tutorial).toBeFunction();
|
||||
});
|
||||
|
||||
it('should export a RootTutorial function', () => {
|
||||
expect(tutorial.RootTutorial).toBeFunction();
|
||||
});
|
||||
|
||||
it('should export a TYPES object', () => {
|
||||
expect(tutorial.TYPES).toBeObject();
|
||||
});
|
||||
|
||||
describe('tutorial.TYPES', () => {
|
||||
it('should have a HTML property', () => {
|
||||
expect(tutorial.TYPES.HTML).toBeNumber();
|
||||
});
|
||||
|
||||
it('should have a MARKDOWN property', () => {
|
||||
expect(tutorial.TYPES.MARKDOWN).toBeNumber();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Tutorial', () => {
|
||||
it('should have a "setParent" method', () => {
|
||||
expect(tutorial.Tutorial.prototype.setParent).toBeFunction();
|
||||
});
|
||||
|
||||
it('should have a "removeChild" method', () => {
|
||||
expect(tutorial.Tutorial.prototype.removeChild).toBeFunction();
|
||||
});
|
||||
|
||||
it('should have an "addChild" method', () => {
|
||||
expect(tutorial.Tutorial.prototype.addChild).toBeFunction();
|
||||
});
|
||||
|
||||
it('should have a "parse" method', () => {
|
||||
expect(tutorial.Tutorial.prototype.parse).toBeFunction();
|
||||
});
|
||||
|
||||
it('should have a "name" property', () => {
|
||||
expect(tute.name).toBe(name);
|
||||
});
|
||||
|
||||
it('should have a "longname" property', () => {
|
||||
expect(tute.longname).toBe(name);
|
||||
});
|
||||
|
||||
it("should have a 'title' property, by default set to to the tutorial's name", () => {
|
||||
expect(tute.title).toBe(name);
|
||||
// Testing of overriding a tutorial's title in its JSON file is
|
||||
// covered in tutorial/resolver.js tests.
|
||||
});
|
||||
|
||||
it("should have a 'content' property set to the tutorial's content", () => {
|
||||
expect(tute.content).toBe(content);
|
||||
});
|
||||
|
||||
it("should have a 'type' property set to the tutorial's type", () => {
|
||||
expect(par.type).toBe(tutorial.TYPES.MARKDOWN);
|
||||
});
|
||||
|
||||
it("should have a 'parent' property, initially null", () => {
|
||||
expect(tute.parent).toBeNull();
|
||||
});
|
||||
|
||||
it("should have a 'children' property, an empty array", () => {
|
||||
expect(tute.children).toBeEmptyArray();
|
||||
});
|
||||
|
||||
describe('setParent', () => {
|
||||
it("adding a parent sets the child's 'parent' property", () => {
|
||||
tute.setParent(par);
|
||||
|
||||
expect(tute.parent).toBe(par);
|
||||
});
|
||||
|
||||
it("adding a parent adds the child to the parent's 'children' property", () => {
|
||||
expect(par.children).toContain(tute);
|
||||
});
|
||||
|
||||
it('re-parenting removes the child from the previous parent', () => {
|
||||
tute.setParent(par2);
|
||||
|
||||
expect(tute.parent).toBe(par2);
|
||||
expect(par2.children).toContain(tute);
|
||||
expect(par.children).not.toContain(tute);
|
||||
});
|
||||
|
||||
it("calling setParent with a null parent unsets the child's parent and removes the child from its previous parent", () => {
|
||||
expect(par2.children).toContain(tute);
|
||||
|
||||
tute.setParent(null);
|
||||
|
||||
expect(tute.parent).toBeNull();
|
||||
expect(par2.children).not.toContain(tute);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addChild', () => {
|
||||
it("adding a child tutorial adds the child to the parent's 'children' property", () => {
|
||||
tute.setParent(null);
|
||||
|
||||
const n = par.children.length;
|
||||
|
||||
par.addChild(tute);
|
||||
|
||||
expect(par.children).toBeArrayOfSize(n + 1);
|
||||
expect(par.children).toContain(tute);
|
||||
});
|
||||
|
||||
it("adding a child tutorial sets the child's parent to to the parent tutorial", () => {
|
||||
expect(tute.parent).toBe(par);
|
||||
});
|
||||
|
||||
it('adding a child tutorial removes the child from its old parent', () => {
|
||||
// tue is currently owned by par; we reparent it to par2
|
||||
expect(tute.parent).toBe(par);
|
||||
|
||||
par2.addChild(tute);
|
||||
|
||||
expect(tute.parent).toBe(par2);
|
||||
expect(par.children).not.toContain(tute);
|
||||
expect(par2.children).toContain(tute);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeChild', () => {
|
||||
function removeChild() {
|
||||
par2.removeChild(par);
|
||||
}
|
||||
|
||||
it('removing a tutorial that is not a child silently passes', () => {
|
||||
const n = par2.children.length;
|
||||
|
||||
expect(removeChild).not.toThrow();
|
||||
expect(par2.children).toBeArrayOfSize(n);
|
||||
});
|
||||
|
||||
it("removing a child removes the child from the parent's 'children' property", () => {
|
||||
tute.setParent(par2);
|
||||
expect(par2.children.length).toBe(1);
|
||||
|
||||
par2.removeChild(tute);
|
||||
|
||||
expect(par2.children).not.toContain(tute);
|
||||
expect(par2.children).toBeEmptyArray();
|
||||
});
|
||||
|
||||
it("removing a child unsets the child's 'parent' property", () => {
|
||||
expect(tute.parent).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('various inheritance tests with addChild, setParent and removeChild', () => {
|
||||
it('parenting and unparenting via addChild, setParent and removeChild makes sure inheritance is set accordingly', () => {
|
||||
// unparent everything.
|
||||
tute.setParent(null);
|
||||
par.setParent(null);
|
||||
par2.setParent(null);
|
||||
|
||||
// let tute belong to par
|
||||
tute.setParent(par);
|
||||
|
||||
expect(tute.parent).toBe(par);
|
||||
expect(par2.children).toBeEmptyArray();
|
||||
expect(par.children).toBeArrayOfSize(1);
|
||||
expect(par.children[0]).toBe(tute);
|
||||
|
||||
// addChild tute to par2. its parent should now be par2, and
|
||||
// it can't be the child of two parents
|
||||
par2.addChild(tute);
|
||||
|
||||
expect(tute.parent).toBe(par2);
|
||||
expect(par.children).toBeEmptyArray();
|
||||
expect(par2.children).toBeArrayOfSize(1);
|
||||
expect(par2.children[0]).toBe(tute);
|
||||
|
||||
// removeChild tute from par2. tute should now be unparented.
|
||||
par2.removeChild(tute);
|
||||
|
||||
expect(tute.parent).toBe(null);
|
||||
expect(par.children).toBeEmptyArray();
|
||||
expect(par2.children).toBeEmptyArray();
|
||||
});
|
||||
});
|
||||
|
||||
describe('parse', () => {
|
||||
const markdownConfig = env.conf.markdown;
|
||||
|
||||
function setMarkdownConfig(config) {
|
||||
env.conf.markdown = config;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
setMarkdownConfig({parser: 'marked'});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
env.conf.markdown = markdownConfig;
|
||||
});
|
||||
|
||||
it('Tutorials with HTML type return content as-is', () => {
|
||||
expect(par2.parse()).toBe('<h2>This is the second parent tutorial</h2>');
|
||||
});
|
||||
|
||||
it('Tutorials with MARKDOWN type go through the markdown parser, respecting configuration options', () => {
|
||||
expect(par.parse()).toBe("<h1>This is the parent tutorial's <em>content & stuff</em> A_B X_Y</h1>");
|
||||
});
|
||||
|
||||
it('Tutorials with MARKDOWN type preserve &/</> entities', () => {
|
||||
expect(markdownEntities.parse())
|
||||
.toBe('<pre>This Markdown tutorial contains HTML entities: & < ></pre>');
|
||||
});
|
||||
|
||||
it('Tutorials with unrecognised type are returned as-is', () => {
|
||||
expect(tute.parse()).toBe(content);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('RootTutorial', () => {
|
||||
it('should inherit from Tutorial', () => {
|
||||
const root = new tutorial.RootTutorial();
|
||||
|
||||
expect(root instanceof tutorial.Tutorial).toBe(true);
|
||||
});
|
||||
|
||||
it('should have a "getByName" method', () => {
|
||||
expect(tutorial.RootTutorial.prototype.getByName).toBeFunction();
|
||||
});
|
||||
|
||||
describe('getByName', () => {
|
||||
let root;
|
||||
|
||||
beforeEach(() => {
|
||||
root = new tutorial.RootTutorial();
|
||||
});
|
||||
|
||||
it('can retrieve tutorials by name', () => {
|
||||
const myTutorial = new tutorial.Tutorial('myTutorial', '', tutorial.TYPES.HTML);
|
||||
|
||||
root._addTutorial(myTutorial);
|
||||
|
||||
expect(root.getByName('myTutorial')).toBe(myTutorial);
|
||||
});
|
||||
|
||||
it('returns nothing for non-existent tutorials', () => {
|
||||
expect(root.getByName('asdf')).toBeFalse();
|
||||
});
|
||||
|
||||
it('uses hasOwnProperty when it checks for the tutorial', () => {
|
||||
expect(root.getByName('prototype')).toBeFalse();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,242 +0,0 @@
|
||||
describe('jsdoc/tutorial/resolver', () => {
|
||||
const env = require('jsdoc/env');
|
||||
const resolver = require('jsdoc/tutorial/resolver');
|
||||
const tutorial = require('jsdoc/tutorial');
|
||||
|
||||
let childNames;
|
||||
let constr;
|
||||
let test;
|
||||
let test2;
|
||||
let test3;
|
||||
let test4;
|
||||
let test6;
|
||||
|
||||
function resetRootTutorial() {
|
||||
resolver.root = new tutorial.RootTutorial();
|
||||
}
|
||||
|
||||
function loadTutorials() {
|
||||
resetRootTutorial();
|
||||
|
||||
resolver.load(`${env.dirname}/test/fixtures/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('should exist', () => {
|
||||
expect(resolver).toBeObject();
|
||||
});
|
||||
|
||||
it('should export an "addTutorial" function', () => {
|
||||
expect(resolver.addTutorial).toBeFunction();
|
||||
});
|
||||
|
||||
it('should export a "load" function', () => {
|
||||
expect(resolver.load).toBeFunction();
|
||||
});
|
||||
|
||||
it('should export a "resolve" function', () => {
|
||||
expect(resolver.resolve).toBeFunction();
|
||||
});
|
||||
|
||||
it('should export a "root" tutorial', () => {
|
||||
expect(resolver.root).toBeObject();
|
||||
expect(resolver.root instanceof tutorial.RootTutorial).toBe(true);
|
||||
});
|
||||
|
||||
it('exported "root" tutorial should export a "getByName" function', () => {
|
||||
expect(resolver.root.getByName).toBeFunction();
|
||||
});
|
||||
|
||||
// note: every time we addTutorial or run the resolver, we are *adding*
|
||||
// to the root tutorial.
|
||||
describe('addTutorial', () => {
|
||||
let tute;
|
||||
|
||||
beforeEach(() => {
|
||||
resetRootTutorial();
|
||||
|
||||
tute = new tutorial.Tutorial('myTutorial', '', tutorial.TYPES.HTML);
|
||||
resolver.addTutorial(tute);
|
||||
});
|
||||
|
||||
afterEach(resetRootTutorial);
|
||||
|
||||
it('should add a default parent of the root tutorial', () => {
|
||||
expect(tute.parent).toBe(resolver.root);
|
||||
});
|
||||
|
||||
it('should be added to the root tutorial as a child', () => {
|
||||
expect(resolver.root.children).toContain(tute);
|
||||
});
|
||||
});
|
||||
|
||||
describe('load', () => {
|
||||
const recurse = env.opts.recurse;
|
||||
|
||||
beforeEach(loadTutorials);
|
||||
|
||||
afterEach(() => {
|
||||
resetRootTutorial();
|
||||
|
||||
env.opts.recurse = recurse;
|
||||
});
|
||||
|
||||
it('does not, by default, recurse into subdirectories', () => {
|
||||
expect(resolver.root.getByName('test_recursive')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('recurses into subdirectories when the --recurse flag is used', () => {
|
||||
let recursiveTute;
|
||||
|
||||
env.opts.recurse = true;
|
||||
loadTutorials();
|
||||
recursiveTute = resolver.root.getByName('test_recursive');
|
||||
|
||||
expect(recursiveTute).toBeObject();
|
||||
expect(recursiveTute instanceof tutorial.Tutorial).toBe(true);
|
||||
});
|
||||
|
||||
it('all tutorials are added, initially as top-level tutorials', () => {
|
||||
// check they were added
|
||||
expect(test).toBeObject();
|
||||
expect(test2).toBeObject();
|
||||
expect(test3).toBeObject();
|
||||
expect(test4).toBeObject();
|
||||
expect(test6).toBeObject();
|
||||
expect(constr).toBeObject();
|
||||
// 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', () => {
|
||||
expect(constr instanceof tutorial.Tutorial).toBe(true);
|
||||
});
|
||||
|
||||
it('non-tutorials are skipped', () => {
|
||||
expect(resolver.root.getByName('multiple')).toBeFalse();
|
||||
expect(resolver.root.getByName('test5')).toBeFalse();
|
||||
});
|
||||
|
||||
it('tutorial types are determined correctly', () => {
|
||||
// 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);
|
||||
});
|
||||
|
||||
it('JSON files with a leading BOM are handled correctly', () => {
|
||||
resetRootTutorial();
|
||||
|
||||
function loadBomTutorials() {
|
||||
resolver.load(`${env.dirname}/test/fixtures/tutorials/bom`);
|
||||
}
|
||||
|
||||
expect(loadBomTutorials).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
// resolve
|
||||
// myTutorial
|
||||
// constructor
|
||||
// test
|
||||
// |- test2
|
||||
// |- test6
|
||||
// |- test3
|
||||
// |- test4
|
||||
describe('resolve', () => {
|
||||
beforeEach(() => {
|
||||
loadTutorials();
|
||||
resolver.resolve();
|
||||
});
|
||||
|
||||
afterEach(resetRootTutorial);
|
||||
|
||||
it('hierarchy is resolved properly no matter how the children property is defined', () => {
|
||||
// root has child 'test'
|
||||
expect(resolver.root.children).toBeArrayOfSize(2);
|
||||
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).toBeArrayOfSize(1);
|
||||
expect(test.children).toContain(test2);
|
||||
expect(test2.parent).toBe(test);
|
||||
|
||||
// test2 has children test3, test6
|
||||
expect(test2.children).toBeArrayOfSize(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).toBeArrayOfSize(1);
|
||||
expect(test3.children).toContain(test4);
|
||||
expect(test4.parent).toBe(test3);
|
||||
});
|
||||
|
||||
it('tutorials without configuration files have titles matching filenames', () => {
|
||||
// test6.xml didn't have a metadata
|
||||
expect(test6.title).toBe('test6');
|
||||
});
|
||||
|
||||
it('tutorials with configuration files have titles as specified in configuration', () => {
|
||||
// test.json had info for just test.json
|
||||
expect(test.title).toBe('Test tutorial');
|
||||
});
|
||||
|
||||
it('multiple tutorials can appear in a configuration file', () => {
|
||||
expect(test2.title).toBe('Test 2');
|
||||
expect(test3.title).toBe('Test 3');
|
||||
expect(test4.title).toBe('Test 4');
|
||||
});
|
||||
|
||||
it('logs an error for missing tutorials', () => {
|
||||
function load() {
|
||||
resolver.load(`${env.dirname}/test/fixtures/tutorials/incomplete`);
|
||||
resolver.resolve();
|
||||
}
|
||||
|
||||
expect(jsdoc.didLog(load, 'error')).toBeTrue();
|
||||
});
|
||||
|
||||
it('logs a warning for duplicate-named tutorials (e.g. test.md, test.html)', () => {
|
||||
function load() {
|
||||
const tute = new tutorial.Tutorial('myTutorial', '', tutorial.TYPES.HTML);
|
||||
|
||||
resolver.addTutorial(tute);
|
||||
resolver.addTutorial(tute);
|
||||
}
|
||||
|
||||
expect(jsdoc.didLog(load, 'warn')).toBeTrue();
|
||||
});
|
||||
|
||||
it('allows tutorials to be defined in one .json file and redefined in another', () => {
|
||||
function load() {
|
||||
resolver.load(`${env.dirname}/test/fixtures/tutorials/duplicateDefined`);
|
||||
resolver.resolve();
|
||||
}
|
||||
|
||||
expect(jsdoc.didLog(load, 'warn')).toBeTrue();
|
||||
// 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')).toBeObject();
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -8,7 +8,6 @@ describe("jsdoc/util/templateHelper", () => {
|
||||
const doclet = require('jsdoc/doclet');
|
||||
const env = require('jsdoc/env');
|
||||
const helper = require('jsdoc/util/templateHelper');
|
||||
const resolver = require('jsdoc/tutorial/resolver');
|
||||
const { taffy } = require('taffydb');
|
||||
|
||||
helper.registerLink('test', 'path/to/test.html');
|
||||
@ -17,10 +16,6 @@ describe("jsdoc/util/templateHelper", () => {
|
||||
expect(helper).toBeObject();
|
||||
});
|
||||
|
||||
it("should export a 'setTutorials' function", () => {
|
||||
expect(helper.setTutorials).toBeFunction();
|
||||
});
|
||||
|
||||
it("should export a 'globalName' property", () => {
|
||||
expect(helper.globalName).toBeString();
|
||||
});
|
||||
@ -97,14 +92,6 @@ describe("jsdoc/util/templateHelper", () => {
|
||||
expect(helper.registerLink).toBeFunction();
|
||||
});
|
||||
|
||||
it("should export a 'tutorialToUrl' function", () => {
|
||||
expect(helper.tutorialToUrl).toBeFunction();
|
||||
});
|
||||
|
||||
it("should export a 'toTutorial' function", () => {
|
||||
expect(helper.toTutorial).toBeFunction();
|
||||
});
|
||||
|
||||
it("should export a 'resolveLinks' function", () => {
|
||||
expect(helper.resolveLinks).toBeFunction();
|
||||
});
|
||||
@ -121,25 +108,6 @@ describe("jsdoc/util/templateHelper", () => {
|
||||
expect(helper.longnamesToTree).toBeFunction();
|
||||
});
|
||||
|
||||
describe("setTutorials", () => {
|
||||
// used in tutorialToUrl, toTutorial.
|
||||
it("setting tutorials to null causes all tutorial lookups to fail", () => {
|
||||
// bit of a dodgy test but the best I can manage. setTutorials doesn't do much.
|
||||
helper.setTutorials(null);
|
||||
|
||||
// should throw error: no 'getByName' in tutorials.
|
||||
expect(() => helper.tutorialToUrl('asdf')).toThrow();
|
||||
});
|
||||
|
||||
it("setting tutorials to the root tutorial object lets lookups work", () => {
|
||||
helper.setTutorials(resolver.root);
|
||||
spyOn(resolver.root, 'getByName');
|
||||
helper.tutorialToUrl('asdf');
|
||||
|
||||
expect(resolver.root.getByName).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("globalName", () => {
|
||||
it("should equal 'global'", () => {
|
||||
expect(helper.globalName).toBe('global');
|
||||
@ -1278,150 +1246,6 @@ describe("jsdoc/util/templateHelper", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("tutorialToUrl", () => {
|
||||
beforeEach(() => {
|
||||
helper.setTutorials(resolver.root);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
helper.setTutorials(null);
|
||||
});
|
||||
|
||||
it('logs an error if the tutorial is missing', () => {
|
||||
function toUrl() {
|
||||
helper.tutorialToUrl('be-a-perfect-person-in-just-three-days');
|
||||
}
|
||||
|
||||
expect(jsdoc.didLog(toUrl, 'error')).toBeTrue();
|
||||
});
|
||||
|
||||
it("logs an error if the tutorial's name is a reserved JS keyword and it doesn't exist", () => {
|
||||
function toUrl() {
|
||||
helper.tutorialToUrl('prototype');
|
||||
}
|
||||
|
||||
expect(jsdoc.didLog(toUrl, 'error')).toBeTrue();
|
||||
});
|
||||
|
||||
it("creates links to tutorials if they exist", () => {
|
||||
let url;
|
||||
|
||||
// load the tutorials we already have for the tutorials tests
|
||||
resolver.load(`${env.dirname}/test/fixtures/tutorials/tutorials`);
|
||||
resolver.resolve();
|
||||
|
||||
url = helper.tutorialToUrl('test');
|
||||
|
||||
expect(url).toBe('tutorial-test.html');
|
||||
});
|
||||
|
||||
it("creates links for tutorials where the name is a reserved JS keyword", () => {
|
||||
const url = helper.tutorialToUrl('constructor');
|
||||
|
||||
expect(url).toBe('tutorial-constructor.html');
|
||||
});
|
||||
|
||||
it("returns the same link if called multiple times on the same tutorial", () => {
|
||||
expect(helper.tutorialToUrl('test2')).toBe(helper.tutorialToUrl('test2'));
|
||||
});
|
||||
});
|
||||
|
||||
describe("toTutorial", () => {
|
||||
beforeEach(() => {
|
||||
helper.setTutorials(resolver.root);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
helper.setTutorials(null);
|
||||
});
|
||||
|
||||
it('logs an error if the first param is missing', () => {
|
||||
function toTutorial() {
|
||||
helper.toTutorial();
|
||||
}
|
||||
|
||||
expect(jsdoc.didLog(toTutorial, 'error')).toBeTrue();
|
||||
});
|
||||
|
||||
// missing tutorials
|
||||
it("returns the tutorial name if it's missing and no missingOpts is provided", () => {
|
||||
let link;
|
||||
|
||||
helper.setTutorials(resolver.root);
|
||||
link = helper.toTutorial('qwerty');
|
||||
|
||||
expect(link).toBe('qwerty');
|
||||
});
|
||||
|
||||
it("returns the tutorial name wrapped in missingOpts.tag if provided and the tutorial is missing", () => {
|
||||
const link = helper.toTutorial('qwerty', 'lkjklqwerty', {tag: 'span'});
|
||||
|
||||
expect(link).toBe('<span>qwerty</span>');
|
||||
});
|
||||
|
||||
it("returns the tutorial name wrapped in missingOpts.tag with class missingOpts.classname if provided and the tutorial is missing", () => {
|
||||
let link = helper.toTutorial('qwerty', 'lkjklqwerty', {classname: 'missing'});
|
||||
|
||||
expect(link).toBe('qwerty');
|
||||
|
||||
link = helper.toTutorial('qwerty', 'lkjklqwerty', {
|
||||
tag: 'span',
|
||||
classname: 'missing'
|
||||
});
|
||||
expect(link).toBe('<span class="missing">qwerty</span>');
|
||||
});
|
||||
|
||||
it("prefixes the tutorial name with missingOpts.prefix if provided and the tutorial is missing", () => {
|
||||
let link = helper.toTutorial('qwerty', 'lkjklqwerty', {
|
||||
tag: 'span',
|
||||
classname: 'missing',
|
||||
prefix: 'TODO-'
|
||||
});
|
||||
|
||||
expect(link).toBe('<span class="missing">TODO-qwerty</span>');
|
||||
|
||||
link = helper.toTutorial('qwerty', 'lkjklqwerty', {prefix: 'TODO-'});
|
||||
|
||||
expect(link).toBe('TODO-qwerty');
|
||||
|
||||
link = helper.toTutorial('qwerty', 'lkjklqwerty', {
|
||||
prefix: 'TODO-',
|
||||
classname: 'missing'
|
||||
});
|
||||
|
||||
expect(link).toBe('TODO-qwerty');
|
||||
});
|
||||
|
||||
// now we do non-missing tutorials.
|
||||
it("returns a link to the tutorial if not missing", () => {
|
||||
let link;
|
||||
|
||||
// load the tutorials we already have for the tutorials tests
|
||||
resolver.load(`${env.dirname}/test/fixtures/tutorials/tutorials`);
|
||||
resolver.resolve();
|
||||
|
||||
link = helper.toTutorial('constructor', 'The Constructor tutorial');
|
||||
|
||||
expect(link).toBe(`<a href="${helper.tutorialToUrl('constructor')}">The Constructor tutorial</a>`);
|
||||
});
|
||||
|
||||
it("uses the tutorial's title for the link text if no content parameter is provided", () => {
|
||||
const link = helper.toTutorial('test');
|
||||
|
||||
expect(link).toBe(`<a href="${helper.tutorialToUrl('test')}">Test tutorial</a>`);
|
||||
});
|
||||
|
||||
it("does not apply any of missingOpts if the tutorial was found", () => {
|
||||
const link = helper.toTutorial('test', '', {
|
||||
tag: 'span',
|
||||
classname: 'missing',
|
||||
prefix: 'TODO-'
|
||||
});
|
||||
|
||||
expect(link).toBe(`<a href="${helper.tutorialToUrl('test')}">Test tutorial</a>`);
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveLinks", () => {
|
||||
let conf;
|
||||
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
describe('@tutorial tag', () => {
|
||||
// these are tests for the block usage, not the inline usage. see util/templateHelper for that.
|
||||
const docSet = jsdoc.getDocSetFromFile('test/fixtures/tutorialtag.js');
|
||||
const doc = docSet.getByLongname('x')[0];
|
||||
|
||||
it("adds the listed tutorials to a 'tutorials' array on the doclet", () => {
|
||||
expect(doc.tutorials).toBeArrayOfSize(2);
|
||||
expect(doc.tutorials).toContain('tute1');
|
||||
expect(doc.tutorials).toContain('tute2');
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user