improve test coverage; remove unreachable code; other cleanup

This commit is contained in:
Jeff Williams 2014-05-27 16:54:38 -07:00
parent 43ebd95874
commit 5d308963e5
10 changed files with 117 additions and 94 deletions

View File

@ -69,23 +69,11 @@ function isBefore(beforeRange, afterRange) {
return beforeRange[1] <= afterRange[0]; return beforeRange[1] <= afterRange[0];
} }
// TODO: docs
function isBetween(middleRange, beforeRange, afterRange) {
return isBefore(middleRange, afterRange) && (beforeRange ?
isBefore(beforeRange, middleRange) : true);
}
// TODO: docs // TODO: docs
function isWithin(innerRange, outerRange) { function isWithin(innerRange, outerRange) {
return innerRange[0] >= outerRange[0] && innerRange[1] <= outerRange[1]; return innerRange[0] >= outerRange[0] && innerRange[1] <= outerRange[1];
} }
// TODO: docs
function isLeadingComment(comment, before, after) {
return !!before && !!after && !!canAcceptComment(after) &&
isBefore(before.range, after.range) && isBetween(comment.range, before.range, after.range);
}
// TODO: docs // TODO: docs
function isJsdocComment(comment) { function isJsdocComment(comment) {
return comment && (comment.type === 'Block') && (comment.value[0] === '*'); return comment && (comment.type === 'Block') && (comment.value[0] === '*');
@ -315,11 +303,6 @@ CommentAttacher.prototype._isEligible = function(node) {
return isEligible; return isEligible;
}; };
// TODO: docs
CommentAttacher.prototype._shouldSkipNode = function(node) {
return !isWithin(node.range, this._pendingCommentRange);
};
// TODO: docs // TODO: docs
// TODO: do we ever get multiple candidate nodes? // TODO: do we ever get multiple candidate nodes?
CommentAttacher.prototype.visit = function(node) { CommentAttacher.prototype.visit = function(node) {

View File

@ -38,7 +38,7 @@ dictionary = {
// all the other dictionary functions use normalised names; we should too. // all the other dictionary functions use normalised names; we should too.
_tags[def.title] = def; _tags[def.title] = def;
if (opts.isNamespace) { if (opts && opts.isNamespace) {
_namespaces.push(def.title); _namespaces.push(def.title);
} }

View File

@ -66,12 +66,7 @@ function regExpFactory(tagName, prefix, suffix) {
* cases. * cases.
*/ */
exports.isInlineTag = function(string, tagName) { exports.isInlineTag = function(string, tagName) {
try { return regExpFactory(tagName, '^', '$').test(string);
return regExpFactory(tagName, '^', '$').test(string);
}
catch(e) {
return false;
}
}; };
/** /**

View File

@ -37,8 +37,6 @@ function unescapeBraces(text) {
* @return {module:jsdoc/tag/type.TypeExpressionInfo} The type expression and updated tag text. * @return {module:jsdoc/tag/type.TypeExpressionInfo} The type expression and updated tag text.
*/ */
function extractTypeExpression(string) { function extractTypeExpression(string) {
string = string || '';
var completeExpression; var completeExpression;
var count = 0; var count = 0;
var position = 0; var position = 0;
@ -110,7 +108,7 @@ function getTagInfo(tagValue, canHaveName, canHaveType) {
if (canHaveType) { if (canHaveType) {
typeOverride = jsdoc.tag.inline.extractInlineTag(text, 'type'); typeOverride = jsdoc.tag.inline.extractInlineTag(text, 'type');
if (typeOverride.tags && typeOverride.tags[0]) { if (typeOverride.tags && typeOverride.tags[0]) {
typeExpression = typeOverride.tags[0].text || typeExpression; typeExpression = typeOverride.tags[0].text;
} }
text = typeOverride.newString; text = typeOverride.newString;
} }
@ -258,20 +256,18 @@ function parseTypeExpression(tagInfo) {
e.message) ); e.message) );
} }
if (parsedType) { tagInfo.type = tagInfo.type.concat( getTypeStrings(parsedType, true) );
tagInfo.type = tagInfo.type.concat( getTypeStrings(parsedType, true) );
// Catharsis and JSDoc use the same names for 'optional' and 'nullable'... // Catharsis and JSDoc use the same names for 'optional' and 'nullable'...
['optional', 'nullable'].forEach(function(key) { ['optional', 'nullable'].forEach(function(key) {
if (parsedType[key] !== null && parsedType[key] !== undefined) { if (parsedType[key] !== null && parsedType[key] !== undefined) {
tagInfo[key] = parsedType[key]; tagInfo[key] = parsedType[key];
}
});
// ...but not 'variable'.
if (parsedType.repeatable !== null && parsedType.repeatable !== undefined) {
tagInfo.variable = parsedType.repeatable;
} }
});
// ...but not 'variable'.
if (parsedType.repeatable !== null && parsedType.repeatable !== undefined) {
tagInfo.variable = parsedType.repeatable;
} }
return tagInfo; return tagInfo;

View File

@ -1,6 +1,17 @@
/*global describe, expect, it */
'use strict';
describe('jsdoc/tag/dictionary', function() { describe('jsdoc/tag/dictionary', function() {
var dictionary = require('jsdoc/tag/dictionary'); var dictionary = require('jsdoc/tag/dictionary');
var tagOptions = {
canHaveValue: true,
isNamespace: true
};
var tagTitle = '!!!testTag!!!';
var tagSynonym = '!!!testTagSynonym!!!';
var tagDef = dictionary.defineTag(tagTitle, tagOptions).synonym(tagSynonym);
it('should exist', function() { it('should exist', function() {
expect(dictionary).toBeDefined(); expect(dictionary).toBeDefined();
expect(typeof dictionary).toBe('object'); expect(typeof dictionary).toBe('object');
@ -26,73 +37,72 @@ describe('jsdoc/tag/dictionary', function() {
expect(typeof dictionary.normalise).toBe('function'); expect(typeof dictionary.normalise).toBe('function');
}); });
// TODO: should really remove this tag from the dictionary after, but how? describe('defineTag', function() {
var tagOptions = { it('returns an object with the correct "title" property', function() {
canHaveValue: true, expect(typeof tagDef).toBe('object');
isNamespace: true expect(tagDef.title).toBeDefined();
}, expect(typeof tagDef.title).toBe('string');
tagTitle = 'testTag', expect(tagDef.title).toBe(dictionary.normalise(tagTitle));
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() { it('returns an object that contains all of the tag properties', function() {
for (var prop in tagOptions) { Object.keys(tagOptions).forEach(function(opt) {
if (tagOptions.hasOwnProperty(prop)) { expect(tagDef[opt]).toBe(tagOptions[opt]);
expect(def[prop]).toBe(tagOptions[prop]); });
} });
it('works correctly without an options object', function() {
var title = '!!!testTagNoOptions!!!';
function makeTag() {
return dictionary.defineTag(title);
} }
expect(makeTag).not.toThrow();
expect(makeTag().title).toBe(dictionary.normalise(title));
}); });
}); });
describe("lookUp", function() { describe('lookUp', function() {
it("retrieves definition when using the tag's canonical name", function() { it("retrieves the definition using the tag's canonical name", function() {
expect(dictionary.lookUp(tagTitle)).toBe(def); expect(dictionary.lookUp(tagTitle)).toBe(tagDef);
}); });
it("retrieves definition when using a synonym", function() { it('retrieves the definition using a synonym for the tag', function() {
expect(dictionary.lookUp(tagSynonym)).toBe(def); expect(dictionary.lookUp(tagSynonym)).toBe(tagDef);
}); });
it("returns FALSE when a tag is not found", function() { it('returns `false` when a tag is not found', function() {
expect(dictionary.lookUp('lkjas1l24jk')).toBe(false); expect(dictionary.lookUp('lkjas1l24jk')).toBe(false);
}); });
}); });
describe("isNamespace", function() { describe('isNamespace', function() {
it("returns whether a tag is a namespace when using its canonical name", function() { it("returns whether a tag is a namespace using the tag's canonical name", function() {
expect(dictionary.isNamespace(tagTitle)).toBe(true); expect(dictionary.isNamespace(tagTitle)).toBe(true);
}); });
it("returns whether a tag is a namespace when using its synonym", function() { it('returns whether a tag is a namespace when using a synonym for the tag', function() {
expect(dictionary.isNamespace(tagSynonym)).toBe(true); expect(dictionary.isNamespace(tagSynonym)).toBe(true);
}); });
it("non-existent tags or non-namespace tags should return false", function() { it('returns `false` for nonexistent tags', function() {
expect(dictionary.isNamespace('see')).toBe(false);
expect(dictionary.isNamespace('lkjasd90034')).toBe(false); expect(dictionary.isNamespace('lkjasd90034')).toBe(false);
}); });
it('returns `false` for non-namespace tags', function() {
expect(dictionary.isNamespace('see')).toBe(false);
});
}); });
describe("normalise", function() { describe('normalise', function() {
it("should return the tag's title if it is not a synonym", function() { it("should return the tag's title if it is not a synonym", function() {
expect(dictionary.normalise('FooBar')).toBe('foobar'); expect(dictionary.normalise('FooBar')).toBe('foobar');
expect(dictionary.normalise(tagTitle)).toBe(def.title); expect(dictionary.normalise(tagTitle)).toBe(tagDef.title);
}); });
it("should return the canonical name of a tag if the synonym is normalised", function() { it('should return the canonical name of a tag if the synonym is normalised', function() {
expect(dictionary.normalise(tagSynonym)).toBe(def.title); expect(dictionary.normalise(tagSynonym)).toBe(tagDef.title);
}); });
}); });
}); });

View File

@ -1,4 +1,5 @@
/*global describe: true, expect: true, it: true, jasmine: true */ /*global describe: true, expect: true, it: true, jasmine: true */
'use strict';
describe('jsdoc/tag/inline', function() { describe('jsdoc/tag/inline', function() {
var jsdoc = { var jsdoc = {
@ -56,7 +57,7 @@ describe('jsdoc/tag/inline', function() {
it('should return false (rather than throwing) with invalid input', function() { it('should return false (rather than throwing) with invalid input', function() {
function badInput() { function badInput() {
return isInlineTag({}); return isInlineTag();
} }
expect(badInput).not.toThrow(); expect(badInput).not.toThrow();

View File

@ -1,4 +1,5 @@
/*global describe: true, expect: true, it: true */ /*global describe, expect, it */
'use strict';
function buildText(type, name, desc) { function buildText(type, name, desc) {
var text = ''; var text = '';

View File

@ -1,4 +1,6 @@
/*global afterEach, beforeEach, describe, env, expect, it, spyOn */ /*global afterEach, beforeEach, describe, env, expect, it, spyOn */
'use strict';
describe('jsdoc/tag/validator', function() { describe('jsdoc/tag/validator', function() {
var doop = require('jsdoc/util/doop'); var doop = require('jsdoc/util/doop');
var logger = require('jsdoc/util/logger'); var logger = require('jsdoc/util/logger');
@ -21,7 +23,11 @@ describe('jsdoc/tag/validator', function() {
var allowUnknown = !!env.conf.tags.allowUnknownTags; var allowUnknown = !!env.conf.tags.allowUnknownTags;
var badTag = { title: 'lkjasdlkjfb' }; var badTag = { title: 'lkjasdlkjfb' };
var badTag2 = new tag.Tag('type', '{string} I am a string!'); var badTag2 = new tag.Tag('type', '{string} I am a string!');
var meta = { filename: 'asdf.js', lineno: 1 }; var meta = {
filename: 'asdf.js',
lineno: 1,
comment: 'Better luck next time.'
};
var goodTag = new tag.Tag('name', 'MyDocletName', meta); // mustHaveValue var goodTag = new tag.Tag('name', 'MyDocletName', meta); // mustHaveValue
var goodTag2 = new tag.Tag('ignore', '', meta); // mustNotHaveValue var goodTag2 = new tag.Tag('ignore', '', meta); // mustNotHaveValue
@ -38,28 +44,28 @@ describe('jsdoc/tag/validator', function() {
env.conf.tags.allowUnknownTags = allowUnknown; env.conf.tags.allowUnknownTags = allowUnknown;
}); });
it("logs an error if the tag is not in the dictionary and conf.tags.allowUnknownTags is false", function() { it('logs an error if the tag is not in the dictionary and conf.tags.allowUnknownTags is false', function() {
env.conf.tags.allowUnknownTags = false; env.conf.tags.allowUnknownTags = false;
validateTag(badTag); validateTag(badTag);
expect(logger.error).toHaveBeenCalled(); expect(logger.error).toHaveBeenCalled();
}); });
it("doesn't log an error if the tag is not in the dictionary and conf.tags.allowUnknownTags is true", function() { it('does not log an error if the tag is not in the dictionary and conf.tags.allowUnknownTags is true', function() {
env.conf.tags.allowUnknownTags = true; env.conf.tags.allowUnknownTags = true;
validateTag(badTag); validateTag(badTag);
expect(logger.error).not.toHaveBeenCalled(); expect(logger.error).not.toHaveBeenCalled();
}); });
it("logs no error for valid tags", function() { it('does not log an error for valid tags', function() {
validateTag(goodTag); validateTag(goodTag);
validateTag(goodTag2); validateTag(goodTag2);
expect(logger.error).not.toHaveBeenCalled(); expect(logger.error).not.toHaveBeenCalled();
}); });
it("logs an error if the tag has no text but .mustHaveValue is true", function() { it('logs an error if the tag has no text but mustHaveValue is true', function() {
var missingName = doop(goodTag); var missingName = doop(goodTag);
missingName.text = null; missingName.text = null;
validateTag(missingName); validateTag(missingName);
@ -67,7 +73,7 @@ describe('jsdoc/tag/validator', function() {
expect(logger.error).toHaveBeenCalled(); expect(logger.error).toHaveBeenCalled();
}); });
it("logs a warning if the tag has text but .mustNotHaveValue is true", function() { it('logs a warning if the tag has text but mustNotHaveValue is true', function() {
var missingText = doop(goodTag2); var missingText = doop(goodTag2);
missingText.mustNotHaveValue = true; missingText.mustNotHaveValue = true;
missingText.text = missingText.text || 'asdf'; missingText.text = missingText.text || 'asdf';
@ -76,10 +82,17 @@ describe('jsdoc/tag/validator', function() {
expect(logger.warn).toHaveBeenCalled(); expect(logger.warn).toHaveBeenCalled();
}); });
it("logs a warning if the tag has a description but .mustNotHaveDescription is true", function() { it('logs a warning if the tag has a description but mustNotHaveDescription is true', function() {
validateTag(badTag2); validateTag(badTag2);
expect(logger.warn).toHaveBeenCalled(); expect(logger.warn).toHaveBeenCalled();
}); });
it('logs meta.comment when present', function() {
env.conf.tags.allowUnknownTags = false;
validateTag(badTag);
expect(logger.error.mostRecentCall.args[0]).toContain(meta.comment);
});
}); });
}); });

View File

@ -1,20 +1,22 @@
/*global beforeEach, describe, expect, it, spyOn */ /*global beforeEach, describe, expect, it, spyOn */
describe("jsdoc/util/error", function() { 'use strict';
describe('jsdoc/util/error', function() {
var error = require('jsdoc/util/error'); var error = require('jsdoc/util/error');
var handle = error.handle; var handle = error.handle;
var logger = require('jsdoc/util/logger'); var logger = require('jsdoc/util/logger');
it("should exist", function() { it('should exist', function() {
expect(error).toBeDefined(); expect(error).toBeDefined();
expect(typeof error).toEqual("object"); expect(typeof error).toBe('object');
}); });
it("should export a 'handle' function", function() { it('should export a "handle" function', function() {
expect(handle).toBeDefined(); expect(handle).toBeDefined();
expect(typeof handle).toEqual("function"); expect(typeof handle).toBe('function');
}); });
describe("handle", function() { describe('handle', function() {
it('should not throw', function() { it('should not throw', function() {
expect(handle).not.toThrow(); expect(handle).not.toThrow();
}); });
@ -25,5 +27,12 @@ describe("jsdoc/util/error", function() {
expect(logger.error).toHaveBeenCalled(); expect(logger.error).toHaveBeenCalled();
}); });
it('should use special formatting for Error instances', function() {
spyOn(logger, 'error');
handle( new Error('Oh no!') );
expect(logger.error).toHaveBeenCalledWith('Error: Oh no!');
});
}); });
}); });

View File

@ -1,4 +1,6 @@
/*global describe: true, env: true, expect: true, it: true, xit: true */ /*global describe, env, expect, it, spyOn */
'use strict';
describe('jsdoc/util/markdown', function() { describe('jsdoc/util/markdown', function() {
var markdown = require('jsdoc/util/markdown'); var markdown = require('jsdoc/util/markdown');
@ -91,5 +93,18 @@ describe('jsdoc/util/markdown', function() {
expect(parser('Visit {@link https://google.com}.')) expect(parser('Visit {@link https://google.com}.'))
.toBe('<p>Visit {@link https://google.com}.</p>'); .toBe('<p>Visit {@link https://google.com}.</p>');
}); });
it('should log an error if an unrecognized Markdown parser is requested', function() {
var logger = require('jsdoc/util/logger');
var parser;
var storage = setMarkdownConf({parser: 'not-a-real-markdown-parser'});
spyOn(logger, 'error');
parser = markdown.getParser();
restoreMarkdownConf(storage);
expect(logger.error).toHaveBeenCalled();
});
}); });
}); });