From 5666f4ccefa03b00aeb39c6e89aa32583b47a444 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Mon, 10 Jul 2017 15:32:27 -0700 Subject: [PATCH] correctly handle aliases that identify instance members, like `@alias Foo#bar` (#1385) --- lib/jsdoc/name.js | 10 +- test/fixtures/alias5.js | 44 +++++ test/specs/documentation/alias.js | 28 +++ test/specs/jsdoc/name.js | 274 +++++++++++++++++------------- 4 files changed, 239 insertions(+), 117 deletions(-) create mode 100644 test/fixtures/alias5.js diff --git a/lib/jsdoc/name.js b/lib/jsdoc/name.js index fbe2c20b..78fb0806 100644 --- a/lib/jsdoc/name.js +++ b/lib/jsdoc/name.js @@ -101,8 +101,16 @@ exports.resolve = function(doclet) { } doclet.name = name; + // does the doclet have an alias that identifies the memberof? if so, use it + if (doclet.alias) { + about = exports.shorten(name); + + if (about.memberof) { + memberof = about.memberof; + } + } // member of a var in an outer scope? - if (name && !memberof && doclet.meta.code && doclet.meta.code.funcscope) { + else if (name && !memberof && doclet.meta.code && doclet.meta.code.funcscope) { name = doclet.longname = doclet.meta.code.funcscope + SCOPE.PUNC.INNER + name; } diff --git a/test/fixtures/alias5.js b/test/fixtures/alias5.js new file mode 100644 index 00000000..38a52e64 --- /dev/null +++ b/test/fixtures/alias5.js @@ -0,0 +1,44 @@ +/** + * Toaster singleton. + * + * @class + */ +var Toaster = (function() { + var instance = null; + + function Toaster() {} + + /** + * Toast an item. + * + * @alias Toaster#toast + * @param {BreadyThing} item - The item to toast. + */ + Toaster.prototype.toast = function(item) {}; + + /** + * Clean the toaster. + * + * @alias clean + * @memberof Toaster + * @instance + */ + Toaster.prototype.clean = function() {}; + + return { + /** + * Get the Toaster instance. + * + * @alias Toaster.getInstance + * @returns {Toaster} The Toaster instance. + */ + getInstance: function() { + if (instance === null) { + instance = new Toaster(); + delete instance.constructor; + } + + return instance; + } + }; +})(); diff --git a/test/specs/documentation/alias.js b/test/specs/documentation/alias.js index d9286750..55a244d9 100644 --- a/test/specs/documentation/alias.js +++ b/test/specs/documentation/alias.js @@ -45,6 +45,34 @@ describe('aliases', function() { expect(jacketClass[0].longname).toBe('module:jacket'); }); + describe('formats', function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/alias5.js'); + var toast = docSet.getByLongname('Toaster#toast')[0]; + var getInstance = docSet.getByLongname('Toaster.getInstance')[0]; + var clean = docSet.getByLongname('Toaster#clean')[0]; + + it('should work when the alias value specifies an instance member', function() { + expect(toast).toBeDefined(); + expect(toast.name).toBe('toast'); + expect(toast.memberof).toBe('Toaster'); + expect(toast.scope).toBe('instance'); + }); + + it('should work when the alias value specifies a static member', function() { + expect(getInstance).toBeDefined(); + expect(getInstance.name).toBe('getInstance'); + expect(getInstance.memberof).toBe('Toaster'); + expect(getInstance.scope).toBe('static'); + }); + + it('should work when the alias value only specifies the short name', function() { + expect(clean).toBeDefined(); + expect(clean.name).toBe('clean'); + expect(clean.memberof).toBe('Toaster'); + expect(clean.scope).toBe('instance'); + }); + }); + it('When a symbol is documented as a static member of , its scope is "global" and not "static".', function() { var docSet = jasmine.getDocSetFromFile('test/fixtures/aliasglobal.js'); var log = docSet.getByLongname('log')[0]; diff --git a/test/specs/jsdoc/name.js b/test/specs/jsdoc/name.js index 94dbe521..e295a485 100644 --- a/test/specs/jsdoc/name.js +++ b/test/specs/jsdoc/name.js @@ -399,143 +399,185 @@ describe('jsdoc/name', function() { return new jsdoc.doclet.Doclet(comment, {}); } - // @event testing. - var event = '@event'; - var memberOf = '@memberof MyClass'; - var name = '@name A'; + describe('aliases', function() { + // If `doclet.alias` is defined, `doclet.name` will be set to the same value by the time + // we call `resolve()`. Therefore, we set both `@alias` and `@name` in these tests. - // Test the basic @event that is not nested. - it('unnested @event gets resolved correctly', function() { - var doclet = makeDoclet([event, name]); + it('can resolve aliases that identify instance members', function() { + var doclet = makeDoclet(['@alias Foo#bar', '@name Foo#bar']); - jsdoc.name.resolve(doclet); + jsdoc.name.resolve(doclet); + console.log(JSON.stringify(doclet, null, 2)); - expect(doclet.name).toEqual('A'); - expect(doclet.memberof).toBeUndefined(); - expect(doclet.longname).toEqual('event:A'); + expect(doclet.name).toBe('bar'); + expect(doclet.memberof).toBe('Foo'); + expect(doclet.scope).toBe('instance'); + expect(doclet.longname).toBe('Foo#bar'); + }); + + it('can resolve aliases that identify static members', function() { + var doclet = makeDoclet(['@alias Foo.bar', '@name Foo.bar']); + + jsdoc.name.resolve(doclet); + + expect(doclet.name).toBe('bar'); + expect(doclet.memberof).toBe('Foo'); + expect(doclet.scope).toBe('static'); + expect(doclet.longname).toBe('Foo.bar'); + }); + + it('works when the alias only specifies the short name', function() { + var doclet = makeDoclet(['@alias bar', '@name bar', '@memberof Foo', '@instance']); + + jsdoc.name.resolve(doclet); + + expect(doclet.name).toBe('bar'); + expect(doclet.memberof).toBe('Foo'); + expect(doclet.scope).toBe('instance'); + expect(doclet.longname).toBe('Foo#bar'); + }); }); - // test all permutations of @event @name [name] @memberof. - it('@event @name @memberof resolves correctly', function() { - var doclet = makeDoclet([event, name, memberOf]); + describe('events', function() { + var event = '@event'; + var memberOf = '@memberof MyClass'; + var name = '@name A'; - jsdoc.name.resolve(doclet); + // Test the basic @event that is not nested. + it('unnested @event gets resolved correctly', function() { + var doclet = makeDoclet([event, name]); - expect(doclet.name).toEqual('A'); - expect(doclet.memberof).toEqual('MyClass'); - expect(doclet.longname).toEqual('MyClass.event:A'); - }); - it('@event @memberof @name resolves correctly', function() { - var doclet = makeDoclet([event, memberOf, name]); + jsdoc.name.resolve(doclet); - jsdoc.name.resolve(doclet); + expect(doclet.name).toEqual('A'); + expect(doclet.memberof).toBeUndefined(); + expect(doclet.longname).toEqual('event:A'); + }); - expect(doclet.name).toEqual('A'); - expect(doclet.memberof).toEqual('MyClass'); - expect(doclet.longname).toEqual('MyClass.event:A'); - }); - it('@name @event @memberof resolves correctly', function() { - var doclet = makeDoclet([name, event, memberOf]); + // test all permutations of @event @name [name] @memberof. + it('@event @name @memberof resolves correctly', function() { + var doclet = makeDoclet([event, name, memberOf]); - jsdoc.name.resolve(doclet); + jsdoc.name.resolve(doclet); - expect(doclet.name).toEqual('A'); - expect(doclet.memberof).toEqual('MyClass'); - expect(doclet.longname).toEqual('MyClass.event:A'); - }); - it('@name @memberof @event resolves correctly', function() { - var doclet = makeDoclet([name, memberOf, event]); + expect(doclet.name).toEqual('A'); + expect(doclet.memberof).toEqual('MyClass'); + expect(doclet.longname).toEqual('MyClass.event:A'); + }); + it('@event @memberof @name resolves correctly', function() { + var doclet = makeDoclet([event, memberOf, name]); - jsdoc.name.resolve(doclet); + jsdoc.name.resolve(doclet); - expect(doclet.name).toEqual('A'); - expect(doclet.memberof).toEqual('MyClass'); - expect(doclet.longname).toEqual('MyClass.event:A'); - }); - it('@memberof @event @name resolves correctly', function() { - var doclet = makeDoclet([memberOf, event, name]); + expect(doclet.name).toEqual('A'); + expect(doclet.memberof).toEqual('MyClass'); + expect(doclet.longname).toEqual('MyClass.event:A'); + }); + it('@name @event @memberof resolves correctly', function() { + var doclet = makeDoclet([name, event, memberOf]); - jsdoc.name.resolve(doclet); + jsdoc.name.resolve(doclet); - expect(doclet.name).toEqual('A'); - expect(doclet.memberof).toEqual('MyClass'); - expect(doclet.longname).toEqual('MyClass.event:A'); - }); - it('@memberof @name @event resolves correctly', function() { - var doclet = makeDoclet([memberOf, name, event]); + expect(doclet.name).toEqual('A'); + expect(doclet.memberof).toEqual('MyClass'); + expect(doclet.longname).toEqual('MyClass.event:A'); + }); + it('@name @memberof @event resolves correctly', function() { + var doclet = makeDoclet([name, memberOf, event]); - jsdoc.name.resolve(doclet); + jsdoc.name.resolve(doclet); - expect(doclet.name).toEqual('A'); - expect(doclet.memberof).toEqual('MyClass'); - expect(doclet.longname).toEqual('MyClass.event:A'); + expect(doclet.name).toEqual('A'); + expect(doclet.memberof).toEqual('MyClass'); + expect(doclet.longname).toEqual('MyClass.event:A'); + }); + it('@memberof @event @name resolves correctly', function() { + var doclet = makeDoclet([memberOf, event, name]); + + jsdoc.name.resolve(doclet); + + expect(doclet.name).toEqual('A'); + expect(doclet.memberof).toEqual('MyClass'); + expect(doclet.longname).toEqual('MyClass.event:A'); + }); + it('@memberof @name @event resolves correctly', function() { + var doclet = makeDoclet([memberOf, name, event]); + + jsdoc.name.resolve(doclet); + + expect(doclet.name).toEqual('A'); + expect(doclet.memberof).toEqual('MyClass'); + expect(doclet.longname).toEqual('MyClass.event:A'); + }); + + // test all permutations of @event [name] @memberof + it('@event [name] @memberof resolves correctly', function() { + var doclet = makeDoclet(['@event A', memberOf]); + + jsdoc.name.resolve(doclet); + + expect(doclet.name).toEqual('A'); + expect(doclet.memberof).toEqual('MyClass'); + expect(doclet.longname).toEqual('MyClass.event:A'); + }); + it('@memberof @event [name] resolves correctly', function() { + var doclet = makeDoclet([memberOf, '@event A']); + + jsdoc.name.resolve(doclet); + + expect(doclet.name).toEqual('A'); + expect(doclet.memberof).toEqual('MyClass'); + expect(doclet.longname).toEqual('MyClass.event:A'); + }); + + // test full @event A.B + it('full @event definition works', function() { + var doclet = makeDoclet(['@event MyClass.A']); + + jsdoc.name.resolve(doclet); + + expect(doclet.name).toEqual('A'); + expect(doclet.memberof).toEqual('MyClass'); + expect(doclet.longname).toEqual('MyClass.event:A'); + }); + it('full @event definition with event: works', function() { + var doclet = makeDoclet(['@event MyClass.event:A']); + + jsdoc.name.resolve(doclet); + + expect(doclet.name).toEqual('event:A'); + expect(doclet.memberof).toEqual('MyClass'); + expect(doclet.longname).toEqual('MyClass.event:A'); + }); + + // a double-nested one just in case + it('@event @name MyClass.EventName @memberof somethingelse works', function() { + var doclet = makeDoclet([event, '@name MyClass.A', '@memberof MyNamespace']); + + jsdoc.name.resolve(doclet); + + expect(doclet.name).toEqual('A'); + expect(doclet.memberof).toEqual('MyNamespace.MyClass'); + expect(doclet.longname).toEqual('MyNamespace.MyClass.event:A'); + }); }); - // test all permutations of @event [name] @memberof - it('@event [name] @memberof resolves correctly', function() { - var doclet = makeDoclet(['@event A', memberOf]); + describe('special names', function() { + // TODO: this test doesn't test what it claims to test! copy-and-paste error? + it('correctly handles a function parameter named "prototype"', function() { + var doclet = makeDoclet([ + '@name Bar.prototype.baz', + '@function', + '@memberof module:foo', + '@param {string} qux' + ]); - jsdoc.name.resolve(doclet); + jsdoc.name.resolve(doclet); - expect(doclet.name).toEqual('A'); - expect(doclet.memberof).toEqual('MyClass'); - expect(doclet.longname).toEqual('MyClass.event:A'); - }); - it('@memberof @event [name] resolves correctly', function() { - var doclet = makeDoclet([memberOf, '@event A']); - - jsdoc.name.resolve(doclet); - - expect(doclet.name).toEqual('A'); - expect(doclet.memberof).toEqual('MyClass'); - expect(doclet.longname).toEqual('MyClass.event:A'); - }); - - // test full @event A.B - it('full @event definition works', function() { - var doclet = makeDoclet(['@event MyClass.A']); - - jsdoc.name.resolve(doclet); - - expect(doclet.name).toEqual('A'); - expect(doclet.memberof).toEqual('MyClass'); - expect(doclet.longname).toEqual('MyClass.event:A'); - }); - it('full @event definition with event: works', function() { - var doclet = makeDoclet(['@event MyClass.event:A']); - - jsdoc.name.resolve(doclet); - - expect(doclet.name).toEqual('event:A'); - expect(doclet.memberof).toEqual('MyClass'); - expect(doclet.longname).toEqual('MyClass.event:A'); - }); - - // a double-nested one just in case - it('@event @name MyClass.EventName @memberof somethingelse works', function() { - var doclet = makeDoclet([event, '@name MyClass.A', '@memberof MyNamespace']); - - jsdoc.name.resolve(doclet); - - expect(doclet.name).toEqual('A'); - expect(doclet.memberof).toEqual('MyNamespace.MyClass'); - expect(doclet.longname).toEqual('MyNamespace.MyClass.event:A'); - }); - - // other cases - it('correctly handles a function parameter named "prototype"', function() { - var doclet = makeDoclet([ - '@name Bar.prototype.baz', - '@function', - '@memberof module:foo', - '@param {string} qux' - ]); - - jsdoc.name.resolve(doclet); - - expect(doclet.name).toBe('baz'); - expect(doclet.memberof).toBe('module:foo.Bar'); - expect(doclet.longname).toBe('module:foo.Bar#baz'); + expect(doclet.name).toBe('baz'); + expect(doclet.memberof).toBe('module:foo.Bar'); + expect(doclet.longname).toBe('module:foo.Bar#baz'); + }); }); }); });