'use strict'; var test = require('tap').test, parse = require('../../lib/parsers/javascript'), remark = require('remark'), visit = require('unist-util-visit'); function pick(obj, props) { if (Array.isArray(props)) { return props.reduce( function(memo, prop) { if (obj[prop] !== undefined) { memo[prop] = obj[prop]; } return memo; }, {} ); } return obj[props]; } function evaluate(fn, filename) { return parse( { file: filename || 'test.js', source: fn instanceof Function ? '(' + fn.toString() + ')' : fn }, {} ); } function addJSDocTag(tree) { visit(tree, 'link', function(node) { node.jsdoc = true; }); return tree; } function removePosition(tree) { visit(tree, function(node) { delete node.position; }); return tree; } test('parse - @abstract', function(t) { t.equal( evaluate(function() { /** @abstract */ })[0].abstract, true ); t.end(); }); test('parse - @access', function(t) { t.equal( evaluate(function() { /** @access public */ })[0].access, 'public', 'access public' ); t.equal( evaluate(function() { /** @access protected */ })[0].access, 'protected', 'access protected' ); t.equal( evaluate(function() { /** @access private */ })[0].access, 'private', 'access private' ); t.end(); }); test('parse - @alias', function(t) { t.end(); }); test('parse - @arg', function(t) { t.end(); }); test('parse - @argument', function(t) { t.end(); }); test('parse - @augments', function(t) { t.equal( evaluate(function() { /** @augments Foo */ })[0].augments[0].name, 'Foo', 'augments' ); t.end(); }); /* * Dossier-style augments tag * https://github.com/google/closure-library/issues/746 */ test('parse - @augments in dossier style', function(t) { t.equal( evaluate(function() { /** @augments {Foo} */ })[0].augments[0].name, 'Foo', 'augments' ); t.end(); }); test('parse - @augments of complex passes through', function(t) { t.deepEqual( evaluate(function() { /** @augments {function()} */ })[0].augments, [] ); t.end(); }); test('parse - @author', function(t) { t.end(); }); test('parse - @borrows', function(t) { t.end(); }); test('parse - @callback', function(t) { t.deepEqual( pick( evaluate(function() { /** @callback name */ })[0], ['kind', 'name', 'type'] ), { name: 'name', kind: 'typedef', type: { type: 'NameExpression', name: 'Function' } } ); t.end(); }); test('parse - @class', function(t) { t.deepEqual( pick( evaluate(function() { /** @class */ })[0], ['kind', 'name', 'type'] ), { kind: 'class' } ); t.deepEqual( pick( evaluate(function() { /** @class name */ })[0], ['kind', 'name', 'type'] ), { name: 'name', kind: 'class' } ); t.deepEqual( pick( evaluate(function() { /** @class {Object} name */ })[0], ['kind', 'name', 'type'] ), { name: 'name', kind: 'class', type: { type: 'NameExpression', name: 'Object' } } ); t.end(); }); test('parse - @classdesc', function(t) { t.deepEqual( evaluate(function() { /** @classdesc test */ })[0].classdesc, remark().parse('test') ); t.end(); }); test('parse - @const', function(t) { t.end(); }); test('parse - @constant', function(t) { t.deepEqual( pick( evaluate(function() { /** @constant */ })[0], ['kind', 'name', 'type'] ), { kind: 'constant' } ); t.deepEqual( pick( evaluate(function() { /** @constant name */ })[0], ['kind', 'name', 'type'] ), { kind: 'constant', name: 'name' } ); t.deepEqual( pick( evaluate(function() { /** @constant {Object} */ })[0], ['kind', 'name', 'type'] ), { kind: 'constant', type: { type: 'NameExpression', name: 'Object' } } ); t.deepEqual( pick( evaluate(function() { /** @constant {Object} name */ })[0], ['kind', 'name', 'type'] ), { kind: 'constant', name: 'name', type: { type: 'NameExpression', name: 'Object' } } ); t.end(); }); test('parse - @constructor', function(t) { t.end(); }); test('parse - @constructs', function(t) { t.end(); }); test('parse - @copyright', function(t) { t.deepEqual( evaluate(function() { /** @copyright test */ })[0].copyright, remark().parse('test') ); t.end(); }); test('parse - @default', function(t) { t.end(); }); test('parse - @defaultvalue', function(t) { t.end(); }); test('parse - @deprecated', function(t) { t.deepEqual( evaluate(function() { /** @deprecated test */ })[0].deprecated, remark().parse('test') ); t.end(); }); test('parse - @desc', function(t) { t.deepEqual( evaluate(function() { /** @desc test */ })[0].description, remark().parse('test') ); t.end(); }); test('parse - @description', function(t) { t.deepEqual( evaluate(function() { /** @description test */ })[0].description, remark().parse('test') ); t.end(); }); test('parse - description', function(t) { t.deepEqual( evaluate(function() { /** test */ })[0].description, remark().parse('test') ); t.end(); }); test('parse - @emits', function(t) { t.end(); }); test('parse - @enum', function(t) { t.end(); }); test('parse - @event', function(t) { t.deepEqual( pick( evaluate(function() { /** @event name */ })[0], ['kind', 'name'] ), { kind: 'event', name: 'name' } ); t.end(); }); test('parse - @example', function(t) { t.deepEqual( evaluate(function() { /** @example test */ })[0].examples[0], { description: 'test' }, 'single line' ); t.deepEqual( evaluate(function() { /** * @example * a * b */ })[0].examples[0], { description: 'a\nb' }, 'multiline' ); t.deepEqual( evaluate(function() { /** * @example caption * a * b */ })[0].examples[0], { description: 'a\nb', caption: remark().parse('caption') }, 'with caption' ); t.deepEqual( evaluate(function() { /** @example */ })[0].errors[0], { message: '@example without code', commentLineNumber: 0 }, 'missing description' ); t.end(); }); test('parse - @exception', function(t) { t.end(); }); test('parse - @exports', function(t) { t.end(); }); test('parse - @extends', function(t) { t.deepEqual( evaluate(function() { /** @extends Foo */ })[0].augments[0].name, 'Foo', 'extends' ); t.end(); }); test('parse - @external', function(t) { t.deepEqual( pick( evaluate(function() { /** @external name */ })[0], ['kind', 'name'] ), { kind: 'external', name: 'name' } ); t.end(); }); test('parse - @file', function(t) { t.deepEqual( pick( evaluate(function() { /** @file */ })[0], ['kind'] ), { kind: 'file' } ); t.deepEqual( pick( evaluate(function() { /** @file desc */ })[0], ['kind', 'description'] ), { kind: 'file', description: remark().parse('desc') } ); t.end(); }); test('parse - @fileoverview', function(t) { t.end(); }); test('parse - @fires', function(t) { t.end(); }); test('parse - @func', function(t) { t.end(); }); test('parse - @function', function(t) { t.deepEqual( pick( evaluate(function() { /** @function */ })[0], ['kind', 'name'] ), { kind: 'function' } ); t.deepEqual( pick( evaluate(function() { /** @function name */ })[0], ['kind', 'name'] ), { kind: 'function', name: 'name' } ); t.end(); }); test('parse - @global', function(t) { t.equal( evaluate(function() { /** @global */ })[0].scope, 'global', 'global' ); t.end(); }); test('parse - @host', function(t) { t.end(); }); test('parse - @ignore', function(t) { t.equal( evaluate(function() { /** @ignore */ })[0].ignore, true ); t.end(); }); test('parse - @implements', function(t) { t.end(); }); test('parse - @inheritdoc', function(t) { t.end(); }); test('parse - @inner', function(t) { t.equal( evaluate(function() { /** @inner*/ })[0].scope, 'inner', 'inner' ); t.end(); }); test('parse - @instance', function(t) { t.equal( evaluate(function() { /** @instance*/ })[0].scope, 'instance', 'instance' ); t.end(); }); test('parse - @interface', function(t) { t.deepEqual( evaluate(function() { /** @interface */ })[0].interface, true, 'anonymous' ); t.deepEqual( evaluate(function() { /** @interface Foo */ })[0].name, 'Foo', 'named' ); t.end(); }); test('parse - @kind', function(t) { t.equal( evaluate(function() { /** @kind class */ })[0].kind, 'class', 'kind' ); t.end(); }); test('parse - @license', function(t) { t.end(); }); test('parse - @listens', function(t) { t.end(); }); test('parse - @member', function(t) { t.deepEqual( pick( evaluate(function() { /** @member */ })[0], ['kind', 'name', 'type'] ), { kind: 'member' } ); t.deepEqual( pick( evaluate(function() { /** @member name */ })[0], ['kind', 'name', 'type'] ), { kind: 'member', name: 'name' } ); t.deepEqual( pick( evaluate(function() { /** @member {Object} */ })[0], ['kind', 'name', 'type'] ), { kind: 'member', type: { type: 'NameExpression', name: 'Object' } } ); t.deepEqual( pick( evaluate(function() { /** @member {Object} name */ })[0], ['kind', 'name', 'type'] ), { kind: 'member', name: 'name', type: { type: 'NameExpression', name: 'Object' } } ); t.end(); }); test('parse - @memberof', function(t) { t.equal( evaluate(function() { /** @memberof test */ })[0].memberof, 'test', 'memberof' ); t.end(); }); test('parse - @method', function(t) { t.end(); }); test('parse - @mixes', function(t) { t.end(); }); test('parse - @mixin', function(t) { t.deepEqual( pick( evaluate(function() { /** @mixin */ })[0], ['kind', 'name'] ), { kind: 'mixin' } ); t.deepEqual( pick( evaluate(function() { /** @mixin name */ })[0], ['kind', 'name'] ), { kind: 'mixin', name: 'name' } ); t.end(); }); test('parse - @module', function(t) { t.deepEqual( pick( evaluate(function() { /** @module */ })[0], ['kind', 'name', 'type'] ), { kind: 'module' } ); t.deepEqual( pick( evaluate(function() { /** @module name */ })[0], ['kind', 'name', 'type'] ), { kind: 'module', name: 'name' } ); t.deepEqual( pick( evaluate(function() { /** @module {Object} name */ })[0], ['kind', 'name', 'type'] ), { kind: 'module', name: 'name', type: { type: 'NameExpression', name: 'Object' } } ); t.end(); }); test('parse - @name', function(t) { t.equal( evaluate(function() { /** @name test */ })[0].name, 'test', 'name' ); t.end(); }); test('parse - @namespace', function(t) { t.deepEqual( pick( evaluate(function() { /** @namespace */ })[0], ['kind', 'name', 'type'] ), { kind: 'namespace' } ); t.deepEqual( pick( evaluate(function() { /** @namespace name */ })[0], ['kind', 'name', 'type'] ), { kind: 'namespace', name: 'name' } ); t.deepEqual( pick( evaluate(function() { /** @namespace {Object} name */ })[0], ['kind', 'name', 'type'] ), { kind: 'namespace', name: 'name', type: { type: 'NameExpression', name: 'Object' } } ); t.end(); }); test('parse - @override', function(t) { t.equal( evaluate(function() { /** @override */ })[0].override, true ); t.end(); }); test('parse - @overview', function(t) { t.end(); }); test('parse - @param', function(t) { t.deepEqual( evaluate(function() { /** @param test */ })[0].params[0], { name: 'test', title: 'param', lineNumber: 0 }, 'name' ); t.deepEqual( evaluate(function() { /** @param {number} test */ })[0].params[0], { name: 'test', title: 'param', type: { name: 'number', type: 'NameExpression' }, lineNumber: 0 }, 'name and type' ); t.deepEqual( evaluate(function() { /** @param {number} test - desc */ })[0].params[0], { name: 'test', title: 'param', type: { name: 'number', type: 'NameExpression' }, description: remark().parse('desc'), lineNumber: 0 }, 'complete' ); t.end(); }); test('parse - @private', function(t) { t.equal( evaluate(function() { /** @private */ })[0].access, 'private', 'private' ); t.end(); }); test('parse - @prop', function(t) { t.deepEqual( evaluate(function() { /** @prop {number} test */ })[0].properties[0], { name: 'test', title: 'property', type: { name: 'number', type: 'NameExpression' }, lineNumber: 0 }, 'name and type' ); t.deepEqual( evaluate(function() { /** @prop {number} test - desc */ })[0].properties[0], { name: 'test', title: 'property', type: { name: 'number', type: 'NameExpression' }, description: remark().parse('desc'), lineNumber: 0 }, 'complete' ); t.end(); }); test('parse - @property', function(t) { t.deepEqual( evaluate(function() { /** @property {number} test */ })[0].properties[0], { name: 'test', title: 'property', type: { name: 'number', type: 'NameExpression' }, lineNumber: 0 }, 'name and type' ); t.deepEqual( evaluate(function() { /** @property {number} test - desc */ })[0].properties[0], { name: 'test', title: 'property', type: { name: 'number', type: 'NameExpression' }, description: remark().parse('desc'), lineNumber: 0 }, 'complete' ); t.end(); }); test('parse - @protected', function(t) { t.equal( evaluate(function() { /** @protected */ })[0].access, 'protected', 'protected' ); t.end(); }); test('parse - @public', function(t) { t.end(); }); test('parse - @readonly', function(t) { t.equal( evaluate(function() { /** @readonly */ })[0].readonly, true ); t.end(); }); test('parse - @requires', function(t) { t.end(); }); test('parse - @return', function(t) { t.deepEqual( evaluate(function() { /** @return test */ })[0].returns[0], { title: 'returns', description: remark().parse('test') }, 'description' ); t.deepEqual( evaluate(function() { /** @return {number} test */ })[0].returns[0], { description: remark().parse('test'), title: 'returns', type: { name: 'number', type: 'NameExpression' } }, 'description and type' ); t.end(); }); test('parse - @returns', function(t) { t.deepEqual( evaluate(function() { /** @returns test */ })[0].returns[0], { title: 'returns', description: remark().parse('test') }, 'description' ); t.deepEqual( evaluate(function() { /** @returns {number} test */ })[0].returns[0], { description: remark().parse('test'), title: 'returns', type: { name: 'number', type: 'NameExpression' } }, 'description and type' ); t.end(); }); test('parse - @see', function(t) { t.deepEqual( evaluate(function() { /** @see test */ })[0].sees, [remark().parse('test')], 'single' ); t.deepEqual( evaluate(function() { /** * @see a * @see b */ })[0].sees, [remark().parse('a'), remark().parse('b')], 'multiple' ); t.end(); }); test('parse - @since', function(t) { t.end(); }); test('parse - @static', function(t) { t.equal( evaluate(function() { /** @static */ })[0].scope, 'static', 'static' ); t.end(); }); test('parse - @summary', function(t) { t.deepEqual( evaluate(function() { /** @summary test */ })[0].summary, remark().parse('test') ); t.end(); }); test('parse - @this', function(t) { t.end(); }); test('parse - @throws', function(t) { t.deepEqual( evaluate(function() { /** @throws desc */ })[0].throws[0], { description: remark().parse('desc') }, 'description' ); t.deepEqual( evaluate(function() { /** @throws {Error} */ })[0].throws[0], { type: { name: 'Error', type: 'NameExpression' } }, 'type' ); t.deepEqual( evaluate(function() { /** @throws {Error} desc */ })[0].throws[0], { type: { name: 'Error', type: 'NameExpression' }, description: remark().parse('desc') }, 'type and description' ); t.deepEqual( evaluate(function() { /** * @throws a * @throws b */ })[0].throws, [ { description: remark().parse('a') }, { description: remark().parse('b') } ], 'multiple' ); t.end(); }); test('parse - @todo', function(t) { t.deepEqual( evaluate(function() { /** @todo test */ })[0].todos, [remark().parse('test')], 'single' ); t.deepEqual( evaluate(function() { /** * @todo a * @todo b */ })[0].todos, [remark().parse('a'), remark().parse('b')], 'multiple' ); t.end(); }); test('parse - @tutorial', function(t) { t.end(); }); test('parse - @type', function(t) { t.end(); }); test('parse - @typedef', function(t) { t.deepEqual( pick( evaluate(function() { /** @typedef {Object} name */ })[0], ['kind', 'name', 'type'] ), { kind: 'typedef', name: 'name', type: { type: 'NameExpression', name: 'Object' } } ); t.end(); }); test('parse - @var', function(t) { t.end(); }); test('parse - @variation', function(t) { t.equal( evaluate(function() { /** @variation 1 */ })[0].variation, 1, 'variation' ); t.end(); }); test('parse - @version', function(t) { t.end(); }); test('parse - @virtual', function(t) { t.end(); }); test('parse - unknown tag', function(t) { t.deepEqual( evaluate(function() { /** @unknown */ })[0].errors[0], { message: 'unknown tag @unknown', commentLineNumber: 0 } ); t.end(); }); test('parse - {@link}', function(t) { t.deepEqual( removePosition( evaluate(function() { /** {@link Foo} */ })[0].description ), addJSDocTag(removePosition(remark().parse('[Foo](Foo)'))) ); t.deepEqual( removePosition( evaluate(function() { /** {@link Foo|text} */ })[0].description ), addJSDocTag(removePosition(remark().parse('[text](Foo)'))) ); t.deepEqual( removePosition( evaluate(function() { /** {@link Foo text} */ })[0].description ), addJSDocTag(removePosition(remark().parse('[text](Foo)'))) ); t.done(); }); test('parse - {@linkcode}', function(t) { t.end(); }); test('parse - {@linkplain}', function(t) { t.end(); }); test('parse - {@tutorial}', function(t) { t.deepEqual( removePosition( evaluate(function() { /** {@tutorial id} */ })[0].description ), { type: 'root', children: [ { type: 'paragraph', children: [ { type: 'tutorial', url: 'id', jsdoc: true, title: null, children: [ { type: 'text', value: 'id' } ] } ] } ] } ); t.deepEqual( removePosition( evaluate(function() { /** {@tutorial id|text} */ })[0].description ), { type: 'root', children: [ { type: 'paragraph', children: [ { type: 'tutorial', url: 'id', jsdoc: true, title: null, children: [ { type: 'text', value: 'text' } ] } ] } ] } ); t.deepEqual( removePosition( evaluate(function() { /** {@tutorial id text} */ })[0].description ), { type: 'root', children: [ { type: 'paragraph', children: [ { type: 'tutorial', url: 'id', jsdoc: true, title: null, children: [ { type: 'text', value: 'text' } ] } ] } ] } ); t.done(); });