mirror of
https://github.com/jsdoc/jsdoc.git
synced 2025-12-08 19:46:11 +00:00
Merge branch 'tests-tag' of github.com:mathematicalcoffee/jsdoc into mathematicalcoffee-tests-tag
Conflicts: test/specs/jsdoc/tag/type.js test/specs/jsdoc/tag/type/closureCompilerType.js
This commit is contained in:
commit
d433b191e3
@ -41,13 +41,14 @@ function trim(text, newlines) {
|
||||
@param {object=} meta
|
||||
*/
|
||||
exports.Tag = function(tagTitle, tagBody, meta) {
|
||||
var tagDef = jsdoc.tag.dictionary.lookUp(tagTitle);
|
||||
meta = meta || {};
|
||||
|
||||
this.originalTitle = trim(tagTitle);
|
||||
|
||||
/** The title part of the tag: @title text */
|
||||
this.title = jsdoc.tag.dictionary.normalise( this.originalTitle );
|
||||
|
||||
var tagDef = jsdoc.tag.dictionary.lookUp(this.title);
|
||||
|
||||
/** The text part of the tag: @title text */
|
||||
this.text = trim(tagBody, tagDef.keepsWhitespace);
|
||||
@ -65,10 +66,15 @@ exports.Tag = function(tagTitle, tagBody, meta) {
|
||||
|
||||
var tagType = jsdoc.tag.type.parse(this.text, tagDef.canHaveName, tagDef.canHaveType);
|
||||
|
||||
if (tagType.type && tagType.type.length) {
|
||||
this.value.type = {
|
||||
names: tagType.type
|
||||
};
|
||||
// It is possible for a tag to *not* have a type but still have
|
||||
// optional or defaultvalue, e.g. '@param [foo]'.
|
||||
// Although tagType.type.length == 0 we should still copy the other properties.
|
||||
if (tagType.type) {
|
||||
if (tagType.type.length) {
|
||||
this.value.type = {
|
||||
names: tagType.type
|
||||
};
|
||||
}
|
||||
this.value.optional = tagType.optional;
|
||||
this.value.nullable = tagType.nullable;
|
||||
this.value.variable = tagType.variable;
|
||||
|
||||
@ -33,13 +33,15 @@ TagDefinition.prototype.synonym = function(synonymName) {
|
||||
dictionary = {
|
||||
/** @function */
|
||||
defineTag: function(title, opts) {
|
||||
_definitions[title] = new TagDefinition(title, opts);
|
||||
var def = new TagDefinition(title, opts);
|
||||
// all the other dictionary functions use normalised names; we should too.
|
||||
_definitions[def.title] = def;
|
||||
|
||||
if (opts.isNamespace) {
|
||||
_namespaces.push(title);
|
||||
_namespaces.push(def.title);
|
||||
}
|
||||
|
||||
return _definitions[title];
|
||||
return _definitions[def.title];
|
||||
},
|
||||
|
||||
/** @function */
|
||||
@ -55,8 +57,11 @@ dictionary = {
|
||||
|
||||
/** @function */
|
||||
isNamespace: function(kind) {
|
||||
if ( _namespaces.indexOf(kind) !== -1) {
|
||||
return true;
|
||||
if (kind) {
|
||||
kind = dictionary.normalise(kind);
|
||||
if ( _namespaces.indexOf(kind) !== -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
6
test/fixtures/paramtag.js
vendored
6
test/fixtures/paramtag.js
vendored
@ -39,3 +39,9 @@ function split(delimiter) {
|
||||
*/
|
||||
function commit(atomic) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [async=true] - whether to be asynchronous
|
||||
*/
|
||||
function request(async) {
|
||||
}
|
||||
|
||||
@ -1,33 +1,162 @@
|
||||
/*global describe: true, env: true, it: true */
|
||||
describe("jsdoc/tag", function() {
|
||||
/*jshint evil: true */
|
||||
|
||||
// TODO: more tests
|
||||
var jsdoc = {
|
||||
tag: require('jsdoc/tag'),
|
||||
dictionary: require('jsdoc/tag/dictionary'),
|
||||
type: require('jsdoc/tag/type')
|
||||
};
|
||||
|
||||
var lenient = !!env.opts.lenient,
|
||||
log = eval(console.log);
|
||||
|
||||
function badTag() {
|
||||
var Tag = require("jsdoc/tag").Tag;
|
||||
var tag = new Tag("name");
|
||||
return tag;
|
||||
}
|
||||
|
||||
afterEach(function() {
|
||||
env.opts.lenient = lenient;
|
||||
console.log = log;
|
||||
it('should exist', function() {
|
||||
expect(jsdoc.tag).toBeDefined();
|
||||
expect(typeof jsdoc.tag).toBe('object');
|
||||
});
|
||||
|
||||
it("throws an exception for bad tags if the lenient option is not enabled", function() {
|
||||
env.opts.lenient = false;
|
||||
|
||||
expect(badTag).toThrow();
|
||||
it('should export a Tag function', function() {
|
||||
expect(jsdoc.tag.Tag).toBeDefined();
|
||||
expect(typeof jsdoc.tag.Tag).toBe('function');
|
||||
});
|
||||
|
||||
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();
|
||||
describe('Tag', function() {
|
||||
var meta = {lineno: 1, filename: 'asdf.js'},
|
||||
desc = 'lalblakd lkjasdlib\n lija',
|
||||
text = '{!number} [foo=1] - ' + desc,
|
||||
textEg = '<caption>Asdf</caption>\n' +
|
||||
' * myFunction(1, 2); // returns 3\n' +
|
||||
' * myFunction(3, 4); // returns 7\n';
|
||||
var tagArg = new jsdoc.tag.Tag('arg ', text, meta), // <-- a symonym of param, space in the title.
|
||||
tagParam = new jsdoc.tag.Tag('param', '[foo=1]', meta), // no type, but has optional and defaultvalue.
|
||||
tagEg = new jsdoc.tag.Tag('example', textEg, meta), // <-- for keepsWhitespace
|
||||
tagType = new jsdoc.tag.Tag('type', 'MyType ', meta); // <-- for onTagText
|
||||
|
||||
it("should have a 'originalTitle' property, a string", function() {
|
||||
expect(tagArg.originalTitle).toBeDefined();
|
||||
expect(typeof tagArg.originalTitle).toBe('string');
|
||||
});
|
||||
|
||||
it("'originalTitle' property should be the initial tag title, trimmed of whitespace", function() {
|
||||
expect(tagArg.originalTitle).toBe('arg');
|
||||
expect(tagEg.originalTitle).toBe('example');
|
||||
});
|
||||
|
||||
it("should have a 'title' property, a string", function() {
|
||||
expect(tagArg.title).toBeDefined();
|
||||
expect(typeof tagArg.title).toBe('string');
|
||||
});
|
||||
|
||||
it("'title' property should be the normalised tag title", function() {
|
||||
expect(tagArg.title).toBe(jsdoc.dictionary.normalise(tagArg.originalTitle));
|
||||
expect(tagEg.title).toBe(jsdoc.dictionary.normalise(tagEg.originalTitle));
|
||||
});
|
||||
|
||||
it("should have a 'text' property. a string", function () {
|
||||
expect(tagArg.text).toBeDefined();
|
||||
expect(typeof tagArg.text).toBe('string');
|
||||
});
|
||||
|
||||
it("should have a 'value' property", function () {
|
||||
expect(tagArg.value).toBeDefined();
|
||||
expect(tagEg.value).toBeDefined();
|
||||
expect(tagType.value).toBeDefined();
|
||||
});
|
||||
|
||||
describe("'text' property", function() {
|
||||
it("'text' property should be the trimmed tag text, with all leading and trailing space removed unless tagDef.keepsWhitespace", function() {
|
||||
// @example has keepsWhitespace, @param doesn't.
|
||||
// should realy use module:jsdoc/tag~trim here but it's private.
|
||||
expect(tagArg.text).toBe(text.replace(/^\s+|\s+$/g, ''));
|
||||
expect(tagEg.text).toBe(textEg.replace(/^[\n\r\f]+|[\n\r\f]+$/g, ''));
|
||||
});
|
||||
|
||||
it("'text' property should have onTagText run on it if it has it.", function() {
|
||||
var def = jsdoc.dictionary.lookUp('type');
|
||||
expect(def.onTagText).toBeDefined();
|
||||
expect(typeof def.onTagText).toBe('function');
|
||||
|
||||
// @type adds {} around the type if necessary.
|
||||
expect(tagType.text).toBeDefined();
|
||||
expect(tagType.text).toBe(def.onTagText('MyType'));
|
||||
});
|
||||
});
|
||||
|
||||
describe("'value' property", function() {
|
||||
it("'value' property should equal tag text if tagDef.canHaveType and canHaveName are both false", function() {
|
||||
// @example can't have type or name
|
||||
expect(typeof tagEg.value).toBe('string');
|
||||
expect(tagEg.value).toBe(tagEg.text);
|
||||
});
|
||||
|
||||
it("'value' property should be an object if tagDef can have type or name", function () {
|
||||
expect(typeof tagType.value).toBe('object');
|
||||
expect(typeof tagArg.value).toBe('object');
|
||||
});
|
||||
|
||||
function verifyTagType(tag) {
|
||||
var def = jsdoc.dictionary.lookUp(tag.title);
|
||||
expect(def).not.toBe(false);
|
||||
var info = jsdoc.type.parse(tag.text, def.canHaveName, def.canHaveType);
|
||||
|
||||
var props_that_should_be_copied = ['optional', 'nullable', 'variable', 'defaultvalue'];
|
||||
for (var i = 0; i < props_that_should_be_copied.length; ++i) {
|
||||
var prop = props_that_should_be_copied[i];
|
||||
if (info.hasOwnProperty(prop)) {
|
||||
expect(tag.value[prop]).toBe(info[prop]);
|
||||
}
|
||||
}
|
||||
if (info.type && info.type.length) {
|
||||
expect(tag.value.type).toBeDefined();
|
||||
expect(typeof tag.value.type).toBe('object');
|
||||
expect(tag.value.type.names).toBeDefined();
|
||||
expect(tag.value.type.names).toEqual(info.type);
|
||||
}
|
||||
}
|
||||
it("if the tag has a type, tag.value should contain the type information", function() {
|
||||
// we assume jsdoc/tag/type.parse works (it has its own tests to verify this);
|
||||
verifyTagType(tagType);
|
||||
verifyTagType(tagArg);
|
||||
verifyTagType(tagParam);
|
||||
});
|
||||
|
||||
it("if the tag has a description beyond the name/type, this should be in tag.value.description", function() {
|
||||
expect(tagType.value.description).not.toBeDefined();
|
||||
|
||||
expect(tagArg.value.description).toBeDefined();
|
||||
expect(tagArg.value.description).toBe(desc);
|
||||
});
|
||||
|
||||
it("if the tag can have a name, it should be stored in tag.value.name", function() {
|
||||
expect(tagArg.value.name).toBeDefined();
|
||||
expect(tagArg.value.name).toBe('foo');
|
||||
|
||||
expect(tagType.value.name).not.toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
// further tests for this sort of thing are in jsdoc/tag/validator.js tests.
|
||||
describe("tag validating", function() {
|
||||
/*jshint evil: true */
|
||||
var lenient = !!env.opts.lenient;
|
||||
|
||||
function badTag() {
|
||||
var tag = new jsdoc.tag.Tag("name");
|
||||
return tag;
|
||||
}
|
||||
|
||||
afterEach(function() {
|
||||
env.opts.lenient = lenient;
|
||||
});
|
||||
|
||||
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() {
|
||||
spyOn(console, 'log');
|
||||
env.opts.lenient = true;
|
||||
|
||||
expect(badTag).not.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,3 +1,98 @@
|
||||
describe("jsdoc/tag/dictionary", function() {
|
||||
//TODO
|
||||
});
|
||||
describe('jsdoc/tag/dictionary', function() {
|
||||
var dictionary = require('jsdoc/tag/dictionary');
|
||||
|
||||
it('should exist', function() {
|
||||
expect(dictionary).toBeDefined();
|
||||
expect(typeof dictionary).toBe('object');
|
||||
});
|
||||
|
||||
it('should export a defineTag function', function() {
|
||||
expect(dictionary.defineTag).toBeDefined();
|
||||
expect(typeof dictionary.defineTag).toBe('function');
|
||||
});
|
||||
|
||||
it('should export a lookUp function', function() {
|
||||
expect(dictionary.lookUp).toBeDefined();
|
||||
expect(typeof dictionary.lookUp).toBe('function');
|
||||
});
|
||||
|
||||
it('should export a isNamespace function', function() {
|
||||
expect(dictionary.isNamespace).toBeDefined();
|
||||
expect(typeof dictionary.isNamespace).toBe('function');
|
||||
});
|
||||
|
||||
it('should export a normalise function', function() {
|
||||
expect(dictionary.normalise).toBeDefined();
|
||||
expect(typeof dictionary.normalise).toBe('function');
|
||||
});
|
||||
|
||||
// TODO: should really remove this tag from the dictionary after, but how?
|
||||
var tagOptions = {
|
||||
canHaveValue: true,
|
||||
isNamespace: true
|
||||
},
|
||||
tagTitle = 'testTag',
|
||||
tagSynonym = 'testTag2';
|
||||
var def = dictionary.defineTag(tagTitle, tagOptions).synonym(tagSynonym);
|
||||
// Should really test TagDefinition but they are private.
|
||||
// Instead, we'll just tests all the properties we expect of it.
|
||||
describe("defineTag", function() {
|
||||
|
||||
// Since TagDefinition is private, I'll just test for its properties here.
|
||||
it("returns an object with 'title' property", function() {
|
||||
expect(typeof def).toBe('object');
|
||||
// how to test?
|
||||
expect(def.title).toBeDefined();
|
||||
expect(typeof def.title).toBe('string');
|
||||
expect(def.title).toBe(dictionary.normalise(tagTitle));
|
||||
});
|
||||
|
||||
it("returned object has all the tag properties copied over", function() {
|
||||
for (var prop in tagOptions) {
|
||||
if (tagOptions.hasOwnProperty(prop)) {
|
||||
expect(def[prop]).toBe(tagOptions[prop]);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("lookUp", function() {
|
||||
it("retrieves definition when using the tag's canonical name", function() {
|
||||
expect(dictionary.lookUp(tagTitle)).toBe(def);
|
||||
});
|
||||
|
||||
it("retrieves definition when using a synonym", function() {
|
||||
expect(dictionary.lookUp(tagSynonym)).toBe(def);
|
||||
});
|
||||
|
||||
it("returns FALSE when a tag is not found", function() {
|
||||
expect(dictionary.lookUp('lkjas1l24jk')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isNamespace", function() {
|
||||
it("returns whether a tag is a namespace when using its canonical name", function() {
|
||||
expect(dictionary.isNamespace(tagTitle)).toBe(true);
|
||||
});
|
||||
|
||||
it("returns whether a tag is a namespace when using its synonym", function() {
|
||||
expect(dictionary.isNamespace(tagSynonym)).toBe(true);
|
||||
});
|
||||
|
||||
it("non-existent tags or non-namespace tags should return false", function() {
|
||||
expect(dictionary.isNamespace('see')).toBe(false);
|
||||
expect(dictionary.isNamespace('lkjasd90034')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("normalise", function() {
|
||||
it("should return the tag's title if it is not a synonym", function() {
|
||||
expect(dictionary.normalise('FooBar')).toBe('foobar');
|
||||
expect(dictionary.normalise(tagTitle)).toBe(def.title);
|
||||
});
|
||||
|
||||
it("should return the canonical name of a tag if the synonym is normalised", function() {
|
||||
expect(dictionary.normalise(tagSynonym)).toBe(def.title);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
15
test/specs/jsdoc/tag/dictionary/definitions.js
Normal file
15
test/specs/jsdoc/tag/dictionary/definitions.js
Normal file
@ -0,0 +1,15 @@
|
||||
describe('jsdoc/tag/dictionary/definitions', function() {
|
||||
var type = require('jsdoc/tag/dictionary/definitions');
|
||||
|
||||
it('should exist', function() {
|
||||
expect(type).toBeDefined();
|
||||
expect(typeof type).toBe('object');
|
||||
});
|
||||
|
||||
it('should export a defineTags function', function() {
|
||||
expect(type.defineTags).toBeDefined();
|
||||
expect(typeof type.defineTags).toBe('function');
|
||||
});
|
||||
// whole bit of dictionary.defineTags...but it just calls dictionary.defineTag
|
||||
// and if I validate that then the rest is automatically validated?
|
||||
});
|
||||
@ -92,6 +92,10 @@ describe('jsdoc/tag/type', function() {
|
||||
desc = '{string=} [foo]';
|
||||
info = jsdoc.tag.type.parse(desc, true, true);
|
||||
expect(info.optional).toBe(true);
|
||||
|
||||
desc = '[foo]';
|
||||
info = jsdoc.tag.type.parse(desc, true, true);
|
||||
expect(info.optional).toBe(true);
|
||||
});
|
||||
|
||||
it('should return the types as an array', function() {
|
||||
|
||||
@ -1,3 +1,94 @@
|
||||
describe("jsdoc/tag/validator", function() {
|
||||
//TODO
|
||||
});
|
||||
describe('jsdoc/tag/validator', function() {
|
||||
var validator = require('jsdoc/tag/validator'),
|
||||
tag = require('jsdoc/tag');
|
||||
|
||||
it('should exist', function() {
|
||||
expect(validator).toBeDefined();
|
||||
expect(typeof validator).toBe('object');
|
||||
});
|
||||
|
||||
it('should export a validate function', function() {
|
||||
expect(validator.validate).toBeDefined();
|
||||
expect(typeof validator.validate).toBe('function');
|
||||
});
|
||||
|
||||
// Note: various Error classes are private so we just test whether *any*
|
||||
// error was thrown, not against particular types (e.g. UnknownTagError).
|
||||
describe('validate', function() {
|
||||
var lenient = !!env.opts.lenient,
|
||||
allowUnknown = !!env.conf.tags.allowUnknownTags,
|
||||
badTag = {title: 'lkjasdlkjfb'},
|
||||
meta = {filename: 'asdf.js', lineno: 1},
|
||||
goodTag = new tag.Tag('name', 'MyDocletName', meta), // mustHaveValue
|
||||
goodTag2 = new tag.Tag('ignore', '', meta); // mustNotHaveValue
|
||||
|
||||
function validateTag(tag) {
|
||||
return function() { validator.validate(tag, meta); };
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
spyOn(console, 'log');
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
env.opts.lenient = lenient;
|
||||
env.conf.tags.allowUnknownTags = allowUnknown;
|
||||
});
|
||||
|
||||
it("throws an error if the tag is not in the dictionary, conf.tags.allowUnknownTags is false and lenient is false", function() {
|
||||
env.opts.lenient = false;
|
||||
env.conf.tags.allowUnknownTags = false;
|
||||
expect(validateTag(badTag)).toThrow();
|
||||
});
|
||||
|
||||
it("throws NO error if the tag is not in the dictionary, conf.tags.allowUnknownTags is false and lenient is true", function() {
|
||||
env.opts.lenient = true;
|
||||
env.conf.tags.allowUnknownTags = false;
|
||||
expect(validateTag(badTag)).not.toThrow();
|
||||
});
|
||||
|
||||
it("doesn't throw an error if the tag is not in the dictionary and conf.tags.allowUnknownTags is true, regardless of lenience", function() {
|
||||
// if it doesn't throw an error with lenient false, then it won't throw it with lenient true (we have
|
||||
// tested lenient already in util/error.js)
|
||||
env.opts.lenient = false;
|
||||
env.conf.tags.allowUnknownTags = true;
|
||||
expect(validateTag(badTag)).not.toThrow();
|
||||
});
|
||||
|
||||
it("throws no error for valid tags", function() {
|
||||
env.opts.lenient = false;
|
||||
expect(validateTag(goodTag)).not.toThrow();
|
||||
expect(validateTag(goodTag2)).not.toThrow();
|
||||
});
|
||||
|
||||
it("throws an error if the tag has no text but .mustHaveValue is true and lenient is false, or none if it's true", function() {
|
||||
// the name tag has .mustHaveValue.
|
||||
var oldText = goodTag.text;
|
||||
delete goodTag.text;
|
||||
|
||||
env.opts.lenient = false;
|
||||
expect(validateTag(goodTag)).toThrow();
|
||||
|
||||
env.opts.lenient = true;
|
||||
expect(validateTag(goodTag)).not.toThrow();
|
||||
|
||||
goodTag.text = oldText;
|
||||
});
|
||||
|
||||
it("throws an error if the tag has text but .mustNotHaveValue is true and lenient is false, or none if it's true", function() {
|
||||
var oldVal = goodTag2.mustNotHaveValue,
|
||||
text = goodTag2.text;
|
||||
goodTag2.mustNotHaveValue = true;
|
||||
goodTag2.text = goodTag2.text || 'asdf';
|
||||
|
||||
env.opts.lenient = false;
|
||||
expect(validateTag(goodTag2)).toThrow();
|
||||
|
||||
env.opts.lenient = true;
|
||||
expect(validateTag(goodTag2)).not.toThrow();
|
||||
|
||||
goodTag2.mustNotHaveValue = oldVal;
|
||||
goodTag2.text = oldVal;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -6,7 +6,8 @@ describe("@param tag", function() {
|
||||
getElement = docSet.getByLongname('getElement')[0],
|
||||
combine = docSet.getByLongname('combine')[0],
|
||||
split = docSet.getByLongname('split')[0],
|
||||
commit = docSet.getByLongname('commit')[0];
|
||||
commit = docSet.getByLongname('commit')[0],
|
||||
request = docSet.getByLongname('request')[0];
|
||||
|
||||
it('When a symbol has an @param tag with a type before the name, the doclet has a params property that includes that param.', function() {
|
||||
expect(typeof find.params).toBe('object');
|
||||
@ -62,7 +63,18 @@ describe("@param tag", function() {
|
||||
expect(commit.params[0].description).toBe('If true make the commit atomic.');
|
||||
});
|
||||
|
||||
it('When a symbol has a @param tag with no type but a name that indicates a default value or optional type, this is copied over to the params property.', function() {
|
||||
expect(typeof request.params).toBe('object');
|
||||
expect(request.params.length).toBe(1);
|
||||
expect(request.params[0].type).toBeUndefined();
|
||||
expect(request.params[0].name).toBe('async');
|
||||
expect(request.params[0].defaultvalue).toBe('true');
|
||||
expect(request.params[0].optional).toBe(true);
|
||||
expect(request.params[0].description).toBe('whether to be asynchronous');
|
||||
});
|
||||
|
||||
it('When a symbol has an @param tag with no name and a name is given in the code, the doclet has a params property that includes that param with the name from the code.', function() {
|
||||
expect(commit.params[0].name).toBe('atomic');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user