448 lines
13 KiB
JavaScript

/*
Copyright 2019 the JSDoc Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
describe('@jsdoc/core.name', () => {
const { name } = require('../../../index');
it('exists', () => {
expect(name).toBeObject();
});
it('has an applyNamespace method', () => {
expect(name.applyNamespace).toBeFunction();
});
it('has a fromParts method', () => {
expect(name.fromParts).toBeFunction();
});
it('has a getBasename method', () => {
expect(name.getBasename).toBeFunction();
});
it('has a hasAncestor method', () => {
expect(name.hasAncestor).toBeFunction();
});
it('has a hasLeadingScope method', () => {
expect(name.hasLeadingScope).toBeFunction();
});
it('has a hasTrailingScope method', () => {
expect(name.hasTrailingScope).toBeFunction();
});
it('has a LONGNAMES enum', () => {
expect(name.LONGNAMES).toBeObject();
});
it('has a longnamesToTree method', () => {
expect(name.longnamesToTree).toBeFunction();
});
it('has a MODULE_NAMESPACE property', () => {
expect(name.MODULE_NAMESPACE).toBeString();
});
it('has a nameIsLongname method', () => {
expect(name.nameIsLongname).toBeFunction();
});
it('has a prototypeToPunc method', () => {
expect(name.prototypeToPunc).toBeFunction();
});
it('has a PUNC_TO_SCOPE enum', () => {
expect(name.PUNC_TO_SCOPE).toBeObject();
});
it('has a SCOPE enum', () => {
expect(name.SCOPE).toBeObject();
});
it('has a SCOPE_TO_PUNC enum', () => {
expect(name.SCOPE_TO_PUNC).toBeObject();
});
it('has a splitNameAndDescription method', () => {
expect(name.splitNameAndDescription).toBeFunction();
});
it('has a stripNamespace method', () => {
expect(name.stripNamespace).toBeFunction();
});
it('has a stripVariation method', () => {
expect(name.stripVariation).toBeFunction();
});
it('has a toParts method', () => {
expect(name.toParts).toBeFunction();
});
describe('applyNamespace', () => {
it('applies the namespace to the name part of the longname', () => {
expect(name.applyNamespace('lib.Panel#open', 'event')).toBe('lib.Panel#event:open');
});
it('applies the namespace to the start of a top-level longname', () => {
expect(name.applyNamespace('math/bigint', 'module')).toBe('module:math/bigint');
});
// TODO(hegemonic): This has never worked
xit('handles longnames with quoted portions', () => {
expect(name.applyNamespace('foo."*don\'t.look~in#here!"', 'event')).toBe(
'foo.event:"*don\'t.look~in#here!"'
);
});
it('handles longnames that already have namespaces', () => {
expect(name.applyNamespace('lib.Panel#event:open', 'event')).toBe('lib.Panel#event:open');
});
});
xdescribe('fromParts', () => {
// TODO: tests
});
describe('getBasename', () => {
it('returns null on empty input', () => {
expect(name.getBasename()).toBeNull();
});
it('returns the original value if it has no punctuation except underscores', () => {
expect(name.getBasename('foo_bar')).toBe('foo_bar');
});
it('returns the basename if the original value has punctuation', () => {
expect(name.getBasename('foo.Bar#baz')).toBe('foo');
});
});
describe('hasAncestor', () => {
it('returns false if no parent is specified', () => {
expect(name.hasAncestor(null, 'foo')).toBe(false);
});
it('returns false if no child is specified', () => {
expect(name.hasAncestor('foo')).toBe(false);
});
it('returns true when the ancestor is the immediate parent', () => {
expect(name.hasAncestor('module:foo', 'module:foo~bar')).toBe(true);
});
it('returns true when the ancestor is not the immediate parent', () => {
expect(name.hasAncestor('module:foo', 'module:foo~bar.Baz#qux')).toBe(true);
});
it('returns false when a non-ancestor is passed in', () => {
expect(name.hasAncestor('module:foo', 'foo')).toBe(false);
});
it('returns false if the parent and child are the same', () => {
expect(name.hasAncestor('module:foo', 'module:foo')).toBe(false);
});
});
describe('hasLeadingScope', () => {
it('returns true if the string starts with a scope character', () => {
expect(name.hasLeadingScope('#foo')).toBeTrue();
});
it('returns false if the string does not start with a scope character', () => {
expect(name.hasLeadingScope('!foo')).toBeFalse();
});
});
describe('hasTrailingScope', () => {
it('returns true if the string ends with a scope character', () => {
expect(name.hasTrailingScope('Foo#')).toBeTrue();
});
it('returns false if the string does not end with a scope character', () => {
expect(name.hasTrailingScope('Foo!')).toBeFalse();
});
});
describe('LONGNAMES', () => {
it('has an ANONYMOUS property', () => {
expect(name.LONGNAMES.ANONYMOUS).toBeString();
});
it('has a GLOBAL property', () => {
expect(name.LONGNAMES.GLOBAL).toBeString();
});
});
xdescribe('longnamesToTree', () => {
// TODO: tests
});
// MODULE_NAMESPACE is just a string, so nothing to test.
xdescribe('nameIsLongname', () => {
// TODO(hegemonic)
});
xdescribe('prototypeToPunc', () => {
// TODO(hegemonic)
});
describe('PUNC_TO_SCOPE', () => {
it('has the same number of properties as SCOPE_TO_PUNC', () => {
expect(Object.keys(name.PUNC_TO_SCOPE).length).toBe(Object.keys(name.SCOPE_TO_PUNC).length);
});
});
describe('SCOPE', () => {
const SCOPE = name.SCOPE;
it('has a NAMES enum', () => {
expect(SCOPE.NAMES).toBeObject();
});
it('has a PUNC enum', () => {
expect(SCOPE.PUNC).toBeObject();
});
describe('NAMES', () => {
it('has a GLOBAL property', () => {
expect(SCOPE.NAMES.GLOBAL).toBeString();
});
it('has an INNER property', () => {
expect(SCOPE.NAMES.INNER).toBeString();
});
it('has an INSTANCE property', () => {
expect(SCOPE.NAMES.INSTANCE).toBeString();
});
it('has a STATIC property', () => {
expect(SCOPE.NAMES.STATIC).toBeString();
});
});
describe('PUNC', () => {
it('has an INNER property', () => {
expect(SCOPE.PUNC.INNER).toBeString();
});
it('has an INSTANCE property', () => {
expect(SCOPE.PUNC.INSTANCE).toBeString();
});
it('has a STATIC property', () => {
expect(SCOPE.PUNC.STATIC).toBeString();
});
});
});
describe('SCOPE_TO_PUNC', () => {
it('has an inner property', () => {
expect(name.SCOPE_TO_PUNC.inner).toBeString();
});
it('has an instance property', () => {
expect(name.SCOPE_TO_PUNC.instance).toBeString();
});
it('has a static property', () => {
expect(name.SCOPE_TO_PUNC.static).toBeString();
});
});
describe('splitNameAndDescription', () => {
// TODO(hegemonic): This has never worked
xit('separates the name from the description', () => {
const parts = name.splitNameAndDescription(
'ns.Page#"last \\"sentence\\"".words~sort(2) - This is a description. '
);
expect(parts.name).toBe('ns.Page#"last \\"sentence\\"".words~sort(2)');
expect(parts.description).toBe('This is a description.');
});
it('strips a separator when it starts on the same line as the name', () => {
const parts = name.splitNameAndDescription('socket - The networking kind, not the wrench.');
expect(parts.name).toBe('socket');
expect(parts.description).toBe('The networking kind, not the wrench.');
});
it('does not strip a separator that is preceded by a line break', () => {
const parts = name.splitNameAndDescription('socket\n - The networking kind, not the wrench.');
expect(parts.name).toBe('socket');
expect(parts.description).toBe('- The networking kind, not the wrench.');
});
it('allows default values to contain square brackets', () => {
const parts = name.splitNameAndDescription(
'[path=["home", "user"]] - Path split into components'
);
expect(parts.name).toBe('[path=["home", "user"]]');
expect(parts.description).toBe('Path split into components');
});
it('allows default values to contain unmatched square brackets inside strings', () => {
const parts = name.splitNameAndDescription(
'[path=["Unmatched begin: ["]] - Path split into components'
);
expect(parts.name).toBe('[path=["Unmatched begin: ["]]');
expect(parts.description).toBe('Path split into components');
});
it('fails gracefully when the default value has an unmatched square bracket', () => {
const parts = name.splitNameAndDescription(
'[path=["home", "user"] - Path split into components'
);
expect(parts).toBeObject();
expect(parts.name).toBe('[path=["home", "user"]');
expect(parts.description).toBe('Path split into components');
});
it('fails gracefully when the default value has an unmatched quote', () => {
const parts = name.splitNameAndDescription(
'[path=["home", "user] - Path split into components'
);
expect(parts).toBeObject();
expect(parts.name).toBe('[path=["home", "user]');
expect(parts.description).toBe('Path split into components');
});
});
describe('stripNamespace', () => {
it('removes the namespace from the longname', () => {
expect(name.stripNamespace('module:foo/bar/baz')).toBe('foo/bar/baz');
});
it('does not remove the namespace from a child member', () => {
expect(name.stripNamespace('foo/bar.baz~event:qux')).toBe('foo/bar.baz~event:qux');
});
it('does not change longnames that do not have a namespace', () => {
expect(name.stripNamespace('Foo#bar')).toBe('Foo#bar');
});
});
describe('stripVariation', () => {
it('does not change longnames with no variation', () => {
expect(name.stripVariation('Foo#bar')).toBe('Foo#bar');
});
it('removes the variation if present', () => {
expect(name.stripVariation('Foo#bar(qux)')).toBe('Foo#bar');
});
});
describe('toParts', () => {
it('breaks up a longname into the correct parts', () => {
const parts = name.toParts('lib.Panel#open');
expect(parts.name).toBe('open');
expect(parts.memberof).toBe('lib.Panel');
expect(parts.scope).toBe('#');
});
it('handles static names', () => {
const parts = name.toParts('elements.selected.getVisible');
expect(parts.name).toBe('getVisible');
expect(parts.memberof).toBe('elements.selected');
expect(parts.scope).toBe('.');
});
it('handles members of a prototype', () => {
const parts = name.toParts('Validator.prototype.$element');
expect(parts.name).toEqual('$element');
expect(parts.memberof).toEqual('Validator');
expect(parts.scope).toEqual('#');
});
it('handles inner names', () => {
const parts = name.toParts('Button~_onclick');
expect(parts.name).toEqual('_onclick');
expect(parts.memberof).toEqual('Button');
expect(parts.scope).toEqual('~');
});
it('handles global names', () => {
const parts = name.toParts('close');
expect(parts.name).toEqual('close');
expect(parts.memberof).toEqual('');
expect(parts.scope).toEqual('');
});
it('handles a single property that uses bracket notation', () => {
const parts = name.toParts('channels["#ops"]#open');
expect(parts.name).toEqual('open');
expect(parts.memberof).toEqual('channels."#ops"');
expect(parts.scope).toEqual('#');
});
it('handles consecutive properties that use bracket notation', () => {
const parts = name.toParts('channels["#bots"]["log.max"]');
expect(parts.name).toEqual('"log.max"');
expect(parts.memberof).toEqual('channels."#bots"');
expect(parts.scope).toEqual('.');
});
it('handles a property that uses single-quoted bracket notation', () => {
const parts = name.toParts("channels['#ops']");
expect(parts.name).toBe("'#ops'");
expect(parts.memberof).toBe('channels');
expect(parts.scope).toBe('.');
});
it('handles double-quoted strings', () => {
const parts = name.toParts('"foo.bar"');
expect(parts.name).toEqual('"foo.bar"');
expect(parts.longname).toEqual('"foo.bar"');
expect(parts.memberof).toEqual('');
expect(parts.scope).toEqual('');
});
it('handles single-quoted strings', () => {
const parts = name.toParts("'foo.bar'");
expect(parts.name).toBe("'foo.bar'");
expect(parts.longname).toBe("'foo.bar'");
expect(parts.memberof).toBe('');
expect(parts.scope).toBe('');
});
it('handles variations', () => {
const parts = name.toParts('anim.fadein(2)');
expect(parts.variation).toEqual('2');
expect(parts.name).toEqual('fadein');
expect(parts.longname).toEqual('anim.fadein(2)');
});
});
});