import parse from '../../src/parsers/javascript.js'; import removePosition from '../../src/remark-remove-position.js'; import { remark } from 'remark'; const remarkParse = remark().use(removePosition).parse; import { visit } from '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; } test('parse - @abstract', function () { expect( evaluate(function () { /** @abstract */ })[0].abstract ).toBe(true); }); test('parse - @access', function () { expect( evaluate(function () { /** @access public */ })[0].access ).toBe('public'); expect( evaluate(function () { /** @access protected */ })[0].access ).toBe('protected'); expect( evaluate(function () { /** @access private */ })[0].access ).toBe('private'); }); test('parse - @alias', function () {}); test('parse - @arg', function () {}); test('parse - @argument', function () {}); test('parse - @async', function () { expect( evaluate(function () { /** @async */ })[0].async ).toBe(true); }); test('parse - @augments', function () { expect( evaluate(function () { /** @augments Foo */ })[0].augments[0].name ).toBe('Foo'); }); test('parse - @description', function () { expect( evaluate(function () { /** * This is a free-form description * @description This tagged description wins, and [is markdown](http://markdown.com). */ })[0].description ).toEqual( remarkParse( 'This tagged description wins, and [is markdown](http://markdown.com).' ) ); }); /* * Dossier-style augments tag * https://github.com/google/closure-library/issues/746 */ test('parse - @augments in dossier style', function () { expect( evaluate(function () { /** @augments {Foo} */ })[0].augments[0].name ).toBe('Foo'); }); test('parse - @augments of complex passes through', function () { expect( evaluate(function () { /** @augments {function()} */ })[0].augments ).toEqual([]); }); test('parse - @author', function () {}); test('parse - @borrows', function () {}); test('parse - @callback', function () { expect( pick( evaluate(function () { /** @callback name */ })[0], ['kind', 'name', 'type'] ) ).toEqual({ name: 'name', kind: 'typedef', type: { type: 'NameExpression', name: 'Function' } }); }); test('parse - @class', function () { expect( pick( evaluate(function () { /** @class */ })[0], ['kind', 'name', 'type'] ) ).toEqual({ kind: 'class' }); expect( pick( evaluate(function () { /** @class name */ })[0], ['kind', 'name', 'type'] ) ).toEqual({ name: 'name', kind: 'class' }); expect( pick( evaluate(function () { /** @class {Object} name */ })[0], ['kind', 'name', 'type'] ) ).toEqual({ name: 'name', kind: 'class', type: { type: 'NameExpression', name: 'Object' } }); }); test('parse - @classdesc', function () { expect( evaluate(function () { /** @classdesc test */ })[0].classdesc ).toEqual(remarkParse('test')); }); test('parse - @const', function () {}); test('parse - @constant', function () { expect( pick( evaluate(function () { /** @constant */ })[0], ['kind', 'name', 'type'] ) ).toEqual({ kind: 'constant' }); expect( pick( evaluate(function () { /** @constant name */ })[0], ['kind', 'name', 'type'] ) ).toEqual({ kind: 'constant', name: 'name' }); expect( pick( evaluate(function () { /** @constant {Object} */ })[0], ['kind', 'name', 'type'] ) ).toEqual({ kind: 'constant', type: { type: 'NameExpression', name: 'Object' } }); expect( pick( evaluate(function () { /** @constant {Object} name */ })[0], ['kind', 'name', 'type'] ) ).toEqual({ kind: 'constant', name: 'name', type: { type: 'NameExpression', name: 'Object' } }); }); test('parse - @constructor', function () {}); test('parse - @constructs', function () {}); test('parse - @copyright', function () { expect( evaluate(function () { /** @copyright test */ })[0].copyright ).toEqual(remarkParse('test')); }); test('parse - @default', function () {}); test('parse - @defaultvalue', function () {}); test('parse - @deprecated', function () { expect( evaluate(function () { /** @deprecated test */ })[0].deprecated ).toEqual(remarkParse('test')); }); test('parse - @desc', function () { expect( evaluate(function () { /** @desc test */ })[0].description ).toEqual(remarkParse('test')); }); test('parse - @description', function () { expect( evaluate(function () { /** @description test */ })[0].description ).toEqual(remarkParse('test')); }); test('parse - description', function () { expect( evaluate(function () { /** test */ })[0].description ).toEqual(remarkParse('test')); }); test('parse - @emits', function () {}); test('parse - @enum', function () { expect( pick( evaluate(function () { /** @enum {string} */ })[0], ['kind', 'type'] ) ).toEqual({ kind: 'enum', type: { type: 'NameExpression', name: 'string' } }); }); test('parse - @event', function () { expect( pick( evaluate(function () { /** @event name */ })[0], ['kind', 'name'] ) ).toEqual({ kind: 'event', name: 'name' }); }); test('parse - @example', function () { expect( evaluate(function () { /** @example test */ })[0].examples[0] ).toEqual({ description: 'test' }); expect( evaluate(function () { /** * @example * a * b */ })[0].examples[0] ).toEqual({ description: 'a\nb' }); expect( evaluate(function () { /** * @example caption * a * b */ })[0].examples[0] ).toEqual({ description: 'a\nb', caption: remarkParse('caption') }); expect( evaluate(function () { /** @example */ })[0].errors[0] ).toEqual({ message: '@example without code', commentLineNumber: 0 }); }); test('parse - @exception', function () {}); test('parse - @exports', function () {}); test('parse - @extends', function () { expect( evaluate(function () { /** @extends Foo */ })[0].augments[0].name ).toEqual('Foo'); }); test('parse - @external', function () { expect( pick( evaluate(function () { /** @external name */ })[0], ['kind', 'name'] ) ).toEqual({ kind: 'external', name: 'name' }); }); test('parse - @file', function () { expect( pick( evaluate(function () { /** @file */ })[0], ['kind'] ) ).toEqual({ kind: 'file' }); expect( pick( evaluate(function () { /** @file desc */ })[0], ['kind', 'description'] ) ).toEqual({ kind: 'file', description: remarkParse('desc') }); }); test('parse - @fileoverview', function () {}); test('parse - @fires', function () {}); test('parse - @func', function () {}); test('parse - @function', function () { expect( pick( evaluate(function () { /** @function */ })[0], ['kind', 'name'] ) ).toEqual({ kind: 'function' }); expect( pick( evaluate(function () { /** @function name */ })[0], ['kind', 'name'] ) ).toEqual({ kind: 'function', name: 'name' }); // When @function takes a name, it is acting as a shorthand for @name and // should detach from code the same way @name does. expect( evaluate(function () { /** @function */ function foo() {} })[0].context.ast ).toBeDefined(); expect( evaluate(function () { /** @function name */ function foo() {} })[0].context.ast ).toBeUndefined(); }); test('parse - @generator', function () { expect( evaluate(function () { /** @generator */ })[0].generator ).toBe(true); }); test('parse - @global', function () { expect( evaluate(function () { /** @global */ })[0].scope ).toBe('global'); }); test('parse - @hideconstructor', function () { expect( evaluate(function () { /** @hideconstructor */ })[0].hideconstructor ).toBe(true); }); test('parse - @host', function () {}); test('parse - @ignore', function () { expect( evaluate(function () { /** @ignore */ })[0].ignore ).toBe(true); }); test('parse - @implements', function () { expect( evaluate(function () { /** @implements {Foo} */ })[0].implements[0].name ).toEqual('Foo'); }); test('parse - @inheritdoc', function () {}); test('parse - @inner', function () { expect( evaluate(function () { /** @inner*/ })[0].scope ).toBe('inner'); }); test('parse - @instance', function () { expect( evaluate(function () { /** @instance*/ })[0].scope ).toBe('instance'); }); test('parse - @interface', function () { expect( evaluate(function () { /** @interface */ })[0].kind ).toEqual('interface'); expect( evaluate(function () { /** @interface Foo */ })[0].name ).toEqual('Foo'); }); test('parse - @kind', function () { expect( evaluate(function () { /** @kind class */ })[0].kind ).toBe('class'); }); test('parse - @license', function () {}); test('parse - @listens', function () {}); test('parse - @member', function () { expect( pick( evaluate(function () { /** @member */ })[0], ['kind', 'name', 'type'] ) ).toEqual({ kind: 'member' }); expect( pick( evaluate(function () { /** @member name */ })[0], ['kind', 'name', 'type'] ) ).toEqual({ kind: 'member', name: 'name' }); expect( pick( evaluate(function () { /** @member {Object} */ })[0], ['kind', 'name', 'type'] ) ).toEqual({ kind: 'member', type: { type: 'NameExpression', name: 'Object' } }); expect( pick( evaluate(function () { /** @member {Object} name */ })[0], ['kind', 'name', 'type'] ) ).toEqual({ kind: 'member', name: 'name', type: { type: 'NameExpression', name: 'Object' } }); }); test('parse - @memberof', function () { expect( evaluate(function () { /** @memberof test */ })[0].memberof ).toBe('test'); }); test('parse - @method', function () {}); test('parse - @mixes', function () {}); test('parse - @mixin', function () { expect( pick( evaluate(function () { /** @mixin */ })[0], ['kind', 'name'] ) ).toEqual({ kind: 'mixin' }); expect( pick( evaluate(function () { /** @mixin name */ })[0], ['kind', 'name'] ) ).toEqual({ kind: 'mixin', name: 'name' }); }); test('parse - @module', function () { expect( pick( evaluate(function () { /** @module */ })[0], ['kind', 'name', 'type'] ) ).toEqual({ kind: 'module' }); expect( pick( evaluate(function () { /** @module name */ })[0], ['kind', 'name', 'type'] ) ).toEqual({ kind: 'module', name: 'name' }); expect( pick( evaluate(function () { /** @module {Object} name */ })[0], ['kind', 'name', 'type'] ) ).toEqual({ kind: 'module', name: 'name', type: { type: 'NameExpression', name: 'Object' } }); }); test('parse - @name', function () { expect( evaluate(function () { /** @name test */ })[0].name ).toBe('test'); expect( evaluate(function () { /** @name foo */ const bar = 0; })[0].context.ast ).toBeUndefined(); }); test('parse - @namespace', function () { expect( pick( evaluate(function () { /** @namespace */ })[0], ['kind', 'name', 'type'] ) ).toEqual({ kind: 'namespace' }); expect( pick( evaluate(function () { /** @namespace name */ })[0], ['kind', 'name', 'type'] ) ).toEqual({ kind: 'namespace', name: 'name' }); expect( pick( evaluate(function () { /** @namespace {Object} name */ })[0], ['kind', 'name', 'type'] ) ).toEqual({ kind: 'namespace', name: 'name', type: { type: 'NameExpression', name: 'Object' } }); }); test('parse - @override', function () { expect( evaluate(function () { /** @override */ })[0].override ).toBe(true); }); test('parse - @overview', function () {}); test('parse - @param', function () { expect( evaluate(function () { /** @param test */ })[0].params[0] ).toEqual({ name: 'test', title: 'param', lineNumber: 0 }); expect( evaluate(function () { /** @param {number} test */ })[0].params[0] ).toEqual({ name: 'test', title: 'param', type: { name: 'number', type: 'NameExpression' }, lineNumber: 0 }); expect( evaluate(function () { /** @param {number} test - desc */ })[0].params[0] ).toEqual({ name: 'test', title: 'param', type: { name: 'number', type: 'NameExpression' }, description: remarkParse('desc'), lineNumber: 0 }); }); test('parse - @private', function () { expect( evaluate(function () { /** @private */ })[0].access ).toBe('private'); }); test('parse - @prop', function () { expect( evaluate(function () { /** @prop {number} test */ })[0].properties[0] ).toEqual({ name: 'test', title: 'property', type: { name: 'number', type: 'NameExpression' }, lineNumber: 0 }); expect( evaluate(function () { /** @prop {number} test - desc */ })[0].properties[0] ).toEqual({ name: 'test', title: 'property', type: { name: 'number', type: 'NameExpression' }, description: remarkParse('desc'), lineNumber: 0 }); }); test('parse - @property', function () { expect( evaluate(function () { /** @property {number} test */ })[0].properties[0] ).toEqual({ name: 'test', title: 'property', type: { name: 'number', type: 'NameExpression' }, lineNumber: 0 }); expect( evaluate(function () { /** @property {number} test - desc */ })[0].properties[0] ).toEqual({ name: 'test', title: 'property', type: { name: 'number', type: 'NameExpression' }, description: remarkParse('desc'), lineNumber: 0 }); }); test('parse - @protected', function () { expect( evaluate(function () { /** @protected */ })[0].access ).toBe('protected'); }); test('parse - @public', function () {}); test('parse - @readonly', function () { expect( evaluate(function () { /** @readonly */ })[0].readonly ).toBe(true); }); test('parse - @requires', function () {}); test('parse - @return', function () { expect( evaluate(function () { /** @return test */ })[0].returns[0] ).toEqual({ title: 'returns', description: remarkParse('test') }); expect( evaluate(function () { /** @return {number} test */ })[0].returns[0] ).toEqual({ description: remarkParse('test'), title: 'returns', type: { name: 'number', type: 'NameExpression' } }); }); test('parse - @returns', function () { expect( evaluate(function () { /** @returns test */ })[0].returns[0] ).toEqual({ title: 'returns', description: remarkParse('test') }); expect( evaluate(function () { /** @returns {number} test */ })[0].returns[0] ).toEqual({ description: remarkParse('test'), title: 'returns', type: { name: 'number', type: 'NameExpression' } }); }); test('parse - @see', function () { expect( evaluate(function () { /** @see [test](#test) */ })[0].sees ).toEqual([ { title: 'sees', description: remarkParse('[test](#test)') } ]); expect( evaluate(function () { /** * @see [a](#a) * @see [b](#b) */ })[0].sees ).toEqual([ { title: 'sees', description: remarkParse('[a](#a)') }, { title: 'sees', description: remarkParse('[b](#b)') } ]); }); test('parse - @since', function () {}); test('parse - @static', function () { expect( evaluate(function () { /** @static */ })[0].scope ).toBe('static'); }); test('parse - @summary', function () { expect( evaluate(function () { /** @summary test */ })[0].summary ).toEqual(remarkParse('test')); }); test('parse - @this', function () {}); test('parse - @throws', function () { expect( evaluate(function () { /** @throws desc */ })[0].throws[0] ).toEqual({ description: remarkParse('desc') }); expect( evaluate(function () { /** @throws {Error} */ })[0].throws[0] ).toEqual({ type: { name: 'Error', type: 'NameExpression' } }); expect( evaluate(function () { /** @throws {Error} desc */ })[0].throws[0] ).toEqual({ type: { name: 'Error', type: 'NameExpression' }, description: remarkParse('desc') }); expect( evaluate(function () { /** * @throws a * @throws b */ })[0].throws ).toEqual([ { description: remarkParse('a') }, { description: remarkParse('b') } ]); }); test('parse - @todo', function () { expect( evaluate(function () { /** @todo test */ })[0].todos ).toEqual([remarkParse('test')]); expect( evaluate(function () { /** * @todo a * @todo b */ })[0].todos ).toEqual([remarkParse('a'), remarkParse('b')]); }); test('parse - @tutorial', function () {}); test('parse - @type', function () {}); test('parse - @typedef', function () { expect( pick( evaluate(function () { /** @typedef {Object} name */ })[0], ['kind', 'name', 'type'] ) ).toEqual({ kind: 'typedef', name: 'name', type: { type: 'NameExpression', name: 'Object' } }); }); test('parse - @var', function () {}); test('parse - @variation', function () { expect( evaluate(function () { /** @variation 1 */ })[0].variation ).toBe(1); }); test('parse - @version', function () {}); test('parse - @virtual', function () {}); test('parse - @yield', function () { expect( evaluate(function () { /** @yield test */ })[0].yields[0] ).toEqual({ title: 'yields', description: remarkParse('test') }); expect( evaluate(function () { /** @yield {number} test */ })[0].yields[0] ).toEqual({ description: remarkParse('test'), title: 'yields', type: { name: 'number', type: 'NameExpression' } }); }); test('parse - @yields', function () { expect( evaluate(function () { /** @yields test */ })[0].yields[0] ).toEqual({ title: 'yields', description: remarkParse('test') }); expect( evaluate(function () { /** @yields {number} test */ })[0].yields[0] ).toEqual({ description: remarkParse('test'), title: 'yields', type: { name: 'number', type: 'NameExpression' } }); }); test('parse - unknown tag', function () { expect( evaluate(function () { /** @unknown */ })[0].errors[0] ).toEqual({ message: 'unknown tag @unknown', commentLineNumber: 0 }); }); test('parse - {@link}', function () { expect( evaluate(function () { /** {@link Foo} */ })[0].description ).toEqual(addJSDocTag(remarkParse('[Foo](Foo)'))); expect( evaluate(function () { /** {@link Foo|text} */ })[0].description ).toEqual(addJSDocTag(remarkParse('[text](Foo)'))); expect( evaluate(function () { /** {@link Foo text} */ })[0].description ).toEqual(addJSDocTag(remarkParse('[text](Foo)'))); }); test('parse - {@linkcode}', function () {}); test('parse - {@linkplain}', function () {}); test('parse - {@tutorial}', function () { expect( evaluate(function () { /** {@tutorial id} */ })[0].description ).toEqual({ type: 'root', children: [ { type: 'paragraph', children: [ { type: 'tutorial', url: 'id', jsdoc: true, title: null, children: [ { type: 'text', value: 'id' } ] } ] } ] }); expect( evaluate(function () { /** {@tutorial id|text} */ })[0].description ).toEqual({ type: 'root', children: [ { type: 'paragraph', children: [ { type: 'tutorial', url: 'id', jsdoc: true, title: null, children: [ { type: 'text', value: 'text' } ] } ] } ] }); expect( evaluate(function () { /** {@tutorial id text} */ })[0].description ).toEqual({ type: 'root', children: [ { type: 'paragraph', children: [ { type: 'tutorial', url: 'id', jsdoc: true, title: null, children: [ { type: 'text', value: 'text' } ] } ] } ] }); });