mirror of
https://github.com/jsdoc/jsdoc.git
synced 2025-12-08 19:46:11 +00:00
1086 lines
37 KiB
JavaScript
1086 lines
37 KiB
JavaScript
/*
|
|
Copyright 2023 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.
|
|
*/
|
|
|
|
import { LONGNAMES } from '@jsdoc/name';
|
|
|
|
import { Doclet } from '../../../lib/doclet.js';
|
|
import * as docletStore from '../../../lib/doclet-store.js';
|
|
|
|
const ANONYMOUS_LONGNAME = LONGNAMES.ANONYMOUS;
|
|
|
|
const { DocletStore } = docletStore;
|
|
|
|
function makeDoclet(comment, meta, env) {
|
|
let doclet;
|
|
|
|
env ??= jsdoc.env;
|
|
doclet = new Doclet(`/**\n${comment.join('\n')}\n*/`, meta, env);
|
|
if (meta?._emitEvent !== false) {
|
|
env.emitter.emit('newDoclet', { doclet });
|
|
}
|
|
|
|
return doclet;
|
|
}
|
|
|
|
describe('@jsdoc/doclet/lib/doclet-store', () => {
|
|
it('exists', () => {
|
|
expect(docletStore).toBeObject();
|
|
});
|
|
|
|
it('has a DocletStore class', () => {
|
|
expect(docletStore.DocletStore).toBeFunction();
|
|
});
|
|
|
|
describe('DocletStore', () => {
|
|
let store;
|
|
|
|
beforeEach(() => {
|
|
store = new DocletStore(jsdoc.env);
|
|
});
|
|
|
|
afterEach(() => {
|
|
store.stopListening();
|
|
});
|
|
|
|
it('is not constructable with no arguments', () => {
|
|
expect(() => new DocletStore()).toThrow();
|
|
});
|
|
|
|
it('is constructable when JSDoc environment is passed in', () => {
|
|
expect(() => new DocletStore(jsdoc.env)).not.toThrow();
|
|
});
|
|
|
|
it('has an `add` method', () => {
|
|
expect(store.add).toBeFunction();
|
|
});
|
|
|
|
it('has an `allDoclets` property', () => {
|
|
expect(store.allDoclets).toBeEmptySet();
|
|
});
|
|
|
|
it('has an `allDocletsByLongname` property', () => {
|
|
expect(store.allDocletsByLongname).toBeEmptyMap();
|
|
});
|
|
|
|
it('has a `commonPathPrefix` property', () => {
|
|
expect(store.commonPathPrefix).toBeEmptyString();
|
|
});
|
|
|
|
it('has a `doclets` property', () => {
|
|
expect(store.doclets).toBeEmptySet();
|
|
});
|
|
|
|
it('has a `docletsByKind` property', () => {
|
|
expect(store.docletsByKind).toBeEmptyMap();
|
|
});
|
|
|
|
it('has a `docletsByLongname` property', () => {
|
|
expect(store.docletsByLongname).toBeEmptyMap();
|
|
});
|
|
|
|
it('has a `docletsByMemberof` property', () => {
|
|
expect(store.docletsByMemberof).toBeEmptyMap();
|
|
});
|
|
|
|
it('has a `docletsWithBorrowed` property', () => {
|
|
expect(store.docletsWithBorrowed).toBeEmptySet();
|
|
});
|
|
|
|
it('has a `globals` property', () => {
|
|
expect(store.globals).toBeEmptySet();
|
|
});
|
|
|
|
it('has a `listenersByListensTo` property', () => {
|
|
expect(store.listenersByListensTo).toBeEmptyMap();
|
|
});
|
|
|
|
it('has a `longnames` property', () => {
|
|
expect(store.longnames).toBeEmptyArray();
|
|
});
|
|
|
|
it('has a `sourcePaths` property', () => {
|
|
expect(store.sourcePaths).toBeEmptyArray();
|
|
});
|
|
|
|
it('has a `startListening` method', () => {
|
|
expect(store.startListening).toBeFunction();
|
|
});
|
|
|
|
it('has a `stopListening` method', () => {
|
|
expect(store.stopListening).toBeFunction();
|
|
});
|
|
|
|
it('has an `unusedDoclets` property', () => {
|
|
expect(store.unusedDoclets).toBeEmptySet();
|
|
});
|
|
|
|
describe('add', () => {
|
|
describe('method', () => {
|
|
it('adds a normal doclet normally', () => {
|
|
// Create a doclet without emitting it, and add it to the store directly.
|
|
store.add(makeDoclet(['@namespace', '@name Foo'], { _emitEvent: false }));
|
|
|
|
expect(Array.from(store.doclets)).toBeArrayOfSize(1);
|
|
expect(store.docletsByKind).toHave('namespace');
|
|
expect(store.docletsByLongname).toHave('Foo');
|
|
});
|
|
|
|
it('tracks changes to a normal doclet', () => {
|
|
// Create a doclet without emitting it.
|
|
const doclet = makeDoclet(['@namespace', '@name Foo'], { _emitEvent: false });
|
|
|
|
store.add(doclet);
|
|
doclet.longname = doclet.name = 'Bar';
|
|
|
|
expect(store.docletsByLongname).not.toHave('Foo');
|
|
expect(store.docletsByLongname).toHave('Bar');
|
|
});
|
|
|
|
it('tracks anonymous doclets only by node ID', () => {
|
|
const anonymousDoclet = makeDoclet(['@function', `@name ${ANONYMOUS_LONGNAME}`], {
|
|
_emitEvent: false,
|
|
code: {
|
|
node: {
|
|
nodeId: 'a',
|
|
},
|
|
},
|
|
});
|
|
|
|
anonymousDoclet.longname = ANONYMOUS_LONGNAME;
|
|
store.add(anonymousDoclet);
|
|
|
|
expect(store.docletsByNodeId.get('a')).toHave(anonymousDoclet);
|
|
expect(store.docletsByLongname).not.toHave(ANONYMOUS_LONGNAME);
|
|
});
|
|
});
|
|
|
|
describe('events with visible doclets', () => {
|
|
let doclet;
|
|
|
|
it('adds the doclet to the list of visible doclets when appropriate', () => {
|
|
doclet = makeDoclet(['@class', '@memberof foo', '@name Bar']);
|
|
|
|
expect(doclet.isVisible()).toBeTrue();
|
|
expect(store.doclets).toHave(doclet);
|
|
});
|
|
|
|
it('never adds visible doclets to the list of unused doclets', () => {
|
|
doclet = makeDoclet(['@class', '@memberof foo', '@name Bar']);
|
|
|
|
expect(doclet.isVisible()).toBeTrue();
|
|
expect(store.unusedDoclets).toBeEmptySet();
|
|
});
|
|
|
|
it('adds visible doclets to the map of doclets by kind', () => {
|
|
doclet = makeDoclet(['@class', '@memberof foo', '@name Bar']);
|
|
|
|
expect(doclet.isVisible()).toBeTrue();
|
|
expect(store.docletsByKind.get('class')).toHave(doclet);
|
|
});
|
|
|
|
it('adds visible doclets to the map of doclets by longname', () => {
|
|
doclet = makeDoclet(['@class', '@memberof foo', '@name Bar']);
|
|
|
|
expect(doclet.isVisible()).toBeTrue();
|
|
expect(store.docletsByLongname.get('foo.Bar')).toHave(doclet);
|
|
});
|
|
|
|
it('adds visible doclets to the map of doclets by memberof when appropriate', () => {
|
|
doclet = makeDoclet(['@class', '@memberof foo', '@name Bar']);
|
|
|
|
expect(doclet.isVisible()).toBeTrue();
|
|
expect(store.docletsByMemberof.get('foo')).toHave(doclet);
|
|
});
|
|
|
|
it('adds doclets that augment others to the list of doclets that augment', () => {
|
|
doclet = makeDoclet(['@class', '@name Qux', '@extends foo.Bar']);
|
|
|
|
expect(store.docletsWithAugments).toHave(doclet);
|
|
});
|
|
|
|
it('does not add doclets that do not augment others to the list of doclets that augment', () => {
|
|
doclet = makeDoclet(['@class', '@name Qux']);
|
|
|
|
expect(store.docletsWithAugments).not.toHave(doclet);
|
|
});
|
|
|
|
it('adds doclets that borrow others to the list of doclets that borrow', () => {
|
|
doclet = makeDoclet(['@class', '@name Qux', '@borrows foo.Bar#baz']);
|
|
|
|
expect(store.docletsWithBorrowed).toHave(doclet);
|
|
});
|
|
|
|
it('does not add doclets that do not borrow others to the list of doclets that borrow', () => {
|
|
doclet = makeDoclet(['@class', '@name Qux']);
|
|
|
|
expect(store.docletsWithBorrowed).not.toHave(doclet);
|
|
});
|
|
|
|
it('adds doclets that implement others to the list of doclets that implement', () => {
|
|
doclet = makeDoclet(['@class', '@name Qux', '@implements IQux']);
|
|
|
|
expect(store.docletsWithImplements).toHave(doclet);
|
|
});
|
|
|
|
it('does not add doclets that do not implement others to the list of doclets that implement', () => {
|
|
doclet = makeDoclet(['@class', '@name Qux']);
|
|
|
|
expect(store.docletsWithImplements).not.toHave(doclet);
|
|
});
|
|
|
|
it('adds doclets that mix others to the list of doclets that mix', () => {
|
|
doclet = makeDoclet(['@class', '@name Qux', '@mixes foo']);
|
|
|
|
expect(store.docletsWithMixes).toHave(doclet);
|
|
});
|
|
|
|
it('does not add doclets that do not mix others to the list of doclets that mix', () => {
|
|
doclet = makeDoclet(['@class', '@name Qux']);
|
|
|
|
expect(store.docletsWithMixes).not.toHave(doclet);
|
|
});
|
|
|
|
it('adds visible doclets to the list of globals when appropriate', () => {
|
|
doclet = makeDoclet(['@function', '@global', '@name baz']);
|
|
|
|
expect(doclet.isVisible()).toBeTrue();
|
|
expect(store.globals).toHave(doclet);
|
|
});
|
|
|
|
it('does not add doclets that are global, but not visible, to the list of globals', () => {
|
|
doclet = makeDoclet(['@function', '@global', '@name baz', '@ignore']);
|
|
|
|
expect(doclet.isVisible()).toBeFalse();
|
|
expect(store.globals).toBeEmptySet();
|
|
});
|
|
|
|
it('adds visible doclets to the map of listeners by listens to when appropriate', () => {
|
|
doclet = makeDoclet(['@function', '@listens event:bar', '@name foo']);
|
|
|
|
expect(doclet.isVisible()).toBeTrue();
|
|
expect(store.listenersByListensTo.get('event:bar')).toHave(doclet);
|
|
});
|
|
|
|
it('adds the source paths for visible doclets to the list of source paths', () => {
|
|
const meta = {
|
|
filename: '/Users/carolr/code/foo.js',
|
|
lineno: 1,
|
|
};
|
|
|
|
doclet = makeDoclet(['@function', '@global', '@name baz'], meta);
|
|
|
|
expect(doclet.isVisible()).toBeTrue();
|
|
expect(store.sourcePaths).toContain('/Users/carolr/code/foo.js');
|
|
});
|
|
});
|
|
|
|
describe('events with unused doclets', () => {
|
|
let doclet;
|
|
|
|
it('adds the doclet to the list of unused doclets when appropriate', () => {
|
|
doclet = makeDoclet(['@class', '@memberof foo', '@name Bar', '@ignore']);
|
|
|
|
expect(doclet.isVisible()).toBeFalse();
|
|
expect(store.unusedDoclets).toHave(doclet);
|
|
});
|
|
|
|
it('does not add unused doclets to the list of visible doclets', () => {
|
|
doclet = makeDoclet(['@class', '@memberof foo', '@name Bar', '@ignore']);
|
|
|
|
expect(doclet.isVisible()).toBeFalse();
|
|
expect(store.doclets).toBeEmptySet();
|
|
});
|
|
|
|
it('does not add unused doclets to the map of doclets by kind', () => {
|
|
doclet = makeDoclet(['@class', '@memberof foo', '@name Bar', '@ignore']);
|
|
|
|
expect(doclet.isVisible()).toBeFalse();
|
|
expect(store.docletsByKind).toBeEmptyMap();
|
|
});
|
|
|
|
it('does not add unused doclets to the map of doclets by longname', () => {
|
|
doclet = makeDoclet(['@class', '@memberof foo', '@name Bar', '@ignore']);
|
|
|
|
expect(doclet.isVisible()).toBeFalse();
|
|
expect(store.docletsByLongname).toBeEmptyMap();
|
|
});
|
|
|
|
it('does not add unused doclets to the map of doclets by memberof', () => {
|
|
doclet = makeDoclet(['@class', '@memberof foo', '@name Bar', '@ignore']);
|
|
|
|
expect(doclet.isVisible()).toBeFalse();
|
|
expect(store.docletsByMemberof).toBeEmptyMap();
|
|
});
|
|
|
|
it('does not add doclets that augment others to the list of doclets that augment', () => {
|
|
doclet = makeDoclet(['@class', '@name Qux', '@extends foo.Bar', '@ignore']);
|
|
|
|
expect(doclet.isVisible()).toBeFalse();
|
|
expect(store.docletsWithAugments).toBeEmptySet();
|
|
});
|
|
|
|
it('does not add doclets that borrow others to the list of doclets that borrow', () => {
|
|
doclet = makeDoclet(['@class', '@name Qux', '@borrows foo.Bar#baz', '@ignore']);
|
|
|
|
expect(doclet.isVisible()).toBeFalse();
|
|
expect(store.docletsWithBorrowed).toBeEmptySet();
|
|
});
|
|
|
|
it('does not add doclets that implement others to the list of doclets that implement', () => {
|
|
doclet = makeDoclet(['@class', '@name Qux', '@implements IQux', '@ignore']);
|
|
|
|
expect(doclet.isVisible()).toBeFalse();
|
|
expect(store.docletsWithImplements).toBeEmptySet();
|
|
});
|
|
|
|
it('does not add doclets that mix others to the list of doclets that mix', () => {
|
|
doclet = makeDoclet(['@class', '@name Qux', '@mixes foo', '@ignore']);
|
|
|
|
expect(doclet.isVisible()).toBeFalse();
|
|
expect(store.docletsWithMixes).toBeEmptySet();
|
|
});
|
|
|
|
it('does not add unused doclets to the list of globals', () => {
|
|
doclet = makeDoclet(['@function', '@global', '@name baz', '@ignore']);
|
|
|
|
expect(doclet.isVisible()).toBeFalse();
|
|
expect(store.globals).toBeEmptySet();
|
|
});
|
|
|
|
it('does not add unused doclets to the map of listeners by listens to', () => {
|
|
doclet = makeDoclet(['@function', '@listens event:bar', '@name foo', '@ignore']);
|
|
|
|
expect(doclet.isVisible()).toBeFalse();
|
|
expect(store.listenersByListensTo).toBeEmptyMap();
|
|
});
|
|
|
|
it('does not add longnames for unused doclets to the list of longnames', () => {
|
|
expect(store.longnames).toBeEmptyArray();
|
|
});
|
|
|
|
it('does not add source paths for unused doclets to the list of source paths', () => {
|
|
const meta = {
|
|
filename: '/Users/carolr/code/foo.js',
|
|
lineno: 1,
|
|
};
|
|
|
|
doclet = makeDoclet(['@function', '@global', '@name baz', '@ignore'], meta);
|
|
|
|
expect(doclet.isVisible()).toBeFalse();
|
|
expect(store.sourcePaths).toBeEmptyArray();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('allDoclets', () => {
|
|
it('contains both visible and hidden doclets', () => {
|
|
const fooDoclet = makeDoclet(['@function', '@name foo']);
|
|
const barDoclet = makeDoclet(['@function', '@name bar', '@ignore']);
|
|
|
|
expect(store.allDoclets.size).toBe(2);
|
|
expect(store.allDoclets).toHave(fooDoclet);
|
|
expect(store.allDoclets).toHave(barDoclet);
|
|
});
|
|
|
|
it('does not contain anonymous doclets that were added directly', () => {
|
|
const anonymousDoclet = makeDoclet(['@function', `@name ${ANONYMOUS_LONGNAME}`], {
|
|
_emitEvent: false,
|
|
code: {
|
|
node: {
|
|
nodeId: 'a',
|
|
},
|
|
},
|
|
});
|
|
|
|
anonymousDoclet.longname = ANONYMOUS_LONGNAME;
|
|
store.add(anonymousDoclet);
|
|
|
|
makeDoclet(['@function', '@name foo']);
|
|
|
|
// Confirm that the anonymous doclet wasn't just ignored.
|
|
expect(store.docletsByNodeId.get('a')).toHave(anonymousDoclet);
|
|
|
|
expect(store.allDoclets.size).toBe(1);
|
|
expect(store.allDoclets).not.toContain(anonymousDoclet);
|
|
});
|
|
});
|
|
|
|
describe('allDocletsByLongname', () => {
|
|
it('contains both visible and hidden doclets', () => {
|
|
const fooDoclet = makeDoclet(['@function', '@name foo']);
|
|
const barDoclet = makeDoclet(['@function', '@name bar', '@ignore']);
|
|
|
|
expect(store.allDocletsByLongname.get('foo')).toHave(fooDoclet);
|
|
expect(store.allDocletsByLongname.get('bar')).toHave(barDoclet);
|
|
});
|
|
|
|
it('does not contain anonymous doclets that were added directly', () => {
|
|
const anonymousDoclet = makeDoclet(['@function', `@name ${ANONYMOUS_LONGNAME}`], {
|
|
_emitEvent: false,
|
|
code: {
|
|
node: {
|
|
nodeId: 'a',
|
|
},
|
|
},
|
|
});
|
|
|
|
anonymousDoclet.longname = ANONYMOUS_LONGNAME;
|
|
store.add(anonymousDoclet);
|
|
|
|
makeDoclet(['@function', '@name foo']);
|
|
|
|
// Confirm that the anonymous doclet wasn't just ignored.
|
|
expect(store.docletsByNodeId.get('a')).toHave(anonymousDoclet);
|
|
|
|
expect(store.allDocletsByLongname).not.toHave(ANONYMOUS_LONGNAME);
|
|
});
|
|
});
|
|
|
|
describe('commonPathPrefix', () => {
|
|
it('returns an empty string if there are no paths', () => {
|
|
expect(store.commonPathPrefix).toBeEmptyString();
|
|
});
|
|
|
|
it('returns the dirname if there is only one path', () => {
|
|
const meta = {
|
|
filename: '/Users/carolr/code/foo.js',
|
|
lineno: 1,
|
|
};
|
|
|
|
makeDoclet(['@function', '@global', '@name baz'], meta);
|
|
|
|
expect(store.commonPathPrefix).toBe('/Users/carolr/code');
|
|
});
|
|
|
|
// Here for the sake of code coverage; it tests the path where the value is already cached.
|
|
it('works twice in a row', () => {
|
|
const meta = {
|
|
filename: '/Users/carolr/code/foo.js',
|
|
lineno: 1,
|
|
};
|
|
|
|
makeDoclet(['@function', '@global', '@name baz'], meta);
|
|
|
|
expect(store.commonPathPrefix).toBe('/Users/carolr/code');
|
|
expect(store.commonPathPrefix).toBe('/Users/carolr/code');
|
|
});
|
|
|
|
it('returns the correct common path if one exists', () => {
|
|
const metaA = {
|
|
filename: '/Users/carolr/code/foo.js',
|
|
lineno: 1,
|
|
};
|
|
const metaB = {
|
|
filename: '/Users/carolr/assets/bar.js',
|
|
lineno: 1,
|
|
};
|
|
|
|
makeDoclet(['@function', '@global', '@name foo'], metaA);
|
|
makeDoclet(['@function', '@global', '@name bar'], metaB);
|
|
|
|
expect(store.commonPathPrefix).toBe('/Users/carolr');
|
|
});
|
|
|
|
it('returns an empty string if there is no common path', () => {
|
|
const metaA = {
|
|
filename: 'C:\\carolr\\code\\foo.js',
|
|
lineno: 1,
|
|
};
|
|
const metaB = {
|
|
filename: 'D:\\code\\bar.js',
|
|
lineno: 1,
|
|
};
|
|
|
|
makeDoclet(['@function', '@global', '@name foo'], metaA);
|
|
makeDoclet(['@function', '@global', '@name bar'], metaB);
|
|
|
|
expect(store.commonPathPrefix).toBeEmptyString();
|
|
});
|
|
|
|
it('returns the correct value if you get the property, then add a path that does not change the result', () => {
|
|
const metaA = {
|
|
filename: '/Users/carolr/code/foo.js',
|
|
lineno: 1,
|
|
};
|
|
const metaB = {
|
|
filename: '/Users/carolr/code/bar.js',
|
|
lineno: 1,
|
|
};
|
|
let prefix1;
|
|
let prefix2;
|
|
|
|
makeDoclet(['@function', '@global', '@name foo'], metaA);
|
|
prefix1 = store.commonPathPrefix;
|
|
makeDoclet(['@function', '@global', '@name bar'], metaB);
|
|
prefix2 = store.commonPathPrefix;
|
|
|
|
expect(prefix1).toBe('/Users/carolr/code');
|
|
expect(prefix2).toBe('/Users/carolr/code');
|
|
});
|
|
|
|
it('returns the correct value if you get the property, then add a path that changes the result', () => {
|
|
const metaA = {
|
|
filename: '/Users/carolr/code/foo.js',
|
|
lineno: 1,
|
|
};
|
|
const metaB = {
|
|
filename: '/Users/carolr/bar.js',
|
|
lineno: 1,
|
|
};
|
|
let prefix1;
|
|
let prefix2;
|
|
|
|
makeDoclet(['@function', '@global', '@name foo'], metaA);
|
|
prefix1 = store.commonPathPrefix;
|
|
makeDoclet(['@function', '@global', '@name bar'], metaB);
|
|
prefix2 = store.commonPathPrefix;
|
|
|
|
expect(prefix1).toBe('/Users/carolr/code');
|
|
expect(prefix2).toBe('/Users/carolr');
|
|
});
|
|
});
|
|
|
|
describe('longnames', () => {
|
|
it('is an empty array if there are no doclets', () => {
|
|
expect(store.longnames).toBeEmptyArray();
|
|
});
|
|
|
|
it('is a list of longnames if there are doclets', () => {
|
|
makeDoclet(['@class', '@memberof foo', '@name Bar']);
|
|
|
|
expect(store.longnames).toEqual(['foo.Bar']);
|
|
});
|
|
});
|
|
|
|
describe('reactivity', () => {
|
|
describe('`access`', () => {
|
|
it('marks a public doclet as unused if it becomes private', () => {
|
|
const doclet = makeDoclet(['@class', '@memberof foo', '@name Bar']);
|
|
|
|
expect(store.unusedDoclets).not.toHave(doclet);
|
|
|
|
doclet.access = 'private';
|
|
|
|
expect(store.unusedDoclets).toHave(doclet);
|
|
});
|
|
|
|
it('marks a private doclet as visible if it becomes public', () => {
|
|
const doclet = makeDoclet(['@class', '@memberof foo', '@name Bar', '@private']);
|
|
|
|
expect(store.unusedDoclets).toHave(doclet);
|
|
|
|
doclet.access = 'public';
|
|
|
|
expect(store.unusedDoclets).not.toHave(doclet);
|
|
});
|
|
});
|
|
|
|
describe('`augments`', () => {
|
|
it('adds a doclet to the list of doclets that augment others when the doclet gains an `augments` value', () => {
|
|
const doclet = makeDoclet(['@class', '@memberof foo', '@name Bar']);
|
|
|
|
expect(store.docletsWithAugments).not.toHave(doclet);
|
|
|
|
doclet.augments = ['Baz'];
|
|
|
|
expect(store.docletsWithAugments).toHave(doclet);
|
|
});
|
|
|
|
it('removes a doclet from the list of doclets that augment others when the doclet loses all `augments` values', () => {
|
|
const doclet = makeDoclet(['@class', '@memberof foo', '@name Bar', '@extends Baz']);
|
|
|
|
expect(store.docletsWithAugments).toHave(doclet);
|
|
|
|
doclet.augments.pop();
|
|
|
|
expect(store.docletsWithAugments).not.toHave(doclet);
|
|
});
|
|
|
|
it('removes a doclet from the list of doclets that augment others when the doclet loses its `augments` property', () => {
|
|
const doclet = makeDoclet(['@class', '@memberof foo', '@name Bar', '@extends Baz']);
|
|
|
|
expect(store.docletsWithAugments).toHave(doclet);
|
|
|
|
doclet.augments = undefined;
|
|
|
|
expect(store.docletsWithAugments).not.toHave(doclet);
|
|
});
|
|
|
|
it('does nothing when a doclet augmented A, but it now augments B instead', () => {
|
|
const doclet = makeDoclet(['@class', '@memberof foo', '@name Bar', '@extends Baz']);
|
|
|
|
expect(store.docletsWithAugments).toHave(doclet);
|
|
|
|
doclet.augments[0] = 'Qux';
|
|
|
|
expect(store.docletsWithAugments).toHave(doclet);
|
|
});
|
|
});
|
|
|
|
describe('`borrowed`', () => {
|
|
it('adds a doclet to the list of doclets that borrow others when the doclet gains a `borrowed` value', () => {
|
|
const doclet = makeDoclet(['@function', '@memberof Foo', '@name bar', '@instance']);
|
|
|
|
expect(store.docletsWithBorrowed).not.toHave(doclet);
|
|
|
|
doclet.borrowed = [{ from: 'Baz#bar' }];
|
|
|
|
expect(store.docletsWithBorrowed).toHave(doclet);
|
|
});
|
|
|
|
it('removes a doclet from the list of doclets that borrow others when the doclet loses all `borrowed` values', () => {
|
|
const doclet = makeDoclet([
|
|
'@function',
|
|
'@memberof Foo',
|
|
'@name bar',
|
|
'@instance',
|
|
'@borrows Baz#bar',
|
|
]);
|
|
|
|
expect(store.docletsWithBorrowed).toHave(doclet);
|
|
|
|
doclet.borrowed.pop();
|
|
|
|
expect(store.docletsWithBorrowed).not.toHave(doclet);
|
|
});
|
|
|
|
it('removes a doclet from the list of doclets that borrow others when the doclet loses its `borrowed` property', () => {
|
|
const doclet = makeDoclet([
|
|
'@function',
|
|
'@memberof Foo',
|
|
'@name bar',
|
|
'@instance',
|
|
'@borrows Baz#bar',
|
|
]);
|
|
|
|
expect(store.docletsWithBorrowed).toHave(doclet);
|
|
|
|
doclet.borrowed = undefined;
|
|
|
|
expect(store.docletsWithBorrowed).not.toHave(doclet);
|
|
});
|
|
|
|
it('does nothing when a doclet borrowed A, but it now borrows B instead', () => {
|
|
const doclet = makeDoclet([
|
|
'@function',
|
|
'@memberof Foo',
|
|
'@name bar',
|
|
'@instance',
|
|
'@borrows Baz#bar',
|
|
]);
|
|
|
|
expect(store.docletsWithBorrowed).toHave(doclet);
|
|
|
|
doclet.borrowed[0] = { from: 'Qux#bar' };
|
|
|
|
expect(store.docletsWithBorrowed).toContain(doclet);
|
|
});
|
|
});
|
|
|
|
describe('`ignore`', () => {
|
|
it('marks the doclet as unused when added', () => {
|
|
const doclet = makeDoclet(['@class', '@memberof foo', '@name Bar']);
|
|
|
|
expect(store.doclets).toHave(doclet);
|
|
expect(store.unusedDoclets).not.toHave(doclet);
|
|
|
|
doclet.ignore = true;
|
|
|
|
expect(store.doclets).not.toHave(doclet);
|
|
expect(store.unusedDoclets).toHave(doclet);
|
|
});
|
|
|
|
it('marks the doclet as visible when removed', () => {
|
|
const doclet = makeDoclet(['@class', '@memberof foo', '@name Bar', '@ignore']);
|
|
|
|
expect(store.doclets).not.toHave(doclet);
|
|
expect(store.unusedDoclets).toHave(doclet);
|
|
|
|
doclet.ignore = undefined;
|
|
|
|
expect(store.doclets).toHave(doclet);
|
|
expect(store.unusedDoclets).not.toHave(doclet);
|
|
});
|
|
});
|
|
|
|
describe('`implements`', () => {
|
|
it('adds a doclet to the list of doclets that implement others when the doclet gains an `implements` value', () => {
|
|
const doclet = makeDoclet(['@class', '@memberof foo', '@name Bar']);
|
|
|
|
expect(store.docletsWithImplements).not.toHave(doclet);
|
|
|
|
doclet.implements = ['IBar'];
|
|
|
|
expect(store.docletsWithImplements).toHave(doclet);
|
|
});
|
|
|
|
it('removes a doclet from the list of doclets that implement others when the doclet loses all `implements` values', () => {
|
|
const doclet = makeDoclet(['@class', '@memberof foo', '@name Bar', '@implements IBar']);
|
|
|
|
expect(store.docletsWithImplements).toHave(doclet);
|
|
|
|
doclet.implements.pop();
|
|
|
|
expect(store.docletsWithImplements).not.toHave(doclet);
|
|
});
|
|
|
|
it('removes a doclet from the list of doclets that implement others when the doclet loses its `implements` property', () => {
|
|
const doclet = makeDoclet(['@class', '@memberof foo', '@name Bar', '@implements IBar']);
|
|
|
|
expect(store.docletsWithImplements).toHave(doclet);
|
|
|
|
doclet.implements = undefined;
|
|
|
|
expect(store.docletsWithImplements).not.toHave(doclet);
|
|
});
|
|
|
|
it('does nothing when a doclet implemented A, but it now implements B instead', () => {
|
|
const doclet = makeDoclet(['@class', '@memberof foo', '@name Bar', '@implements IBar']);
|
|
|
|
expect(store.docletsWithImplements).toHave(doclet);
|
|
|
|
doclet.implements[0] = 'IBaz';
|
|
|
|
expect(store.docletsWithImplements).toHave(doclet);
|
|
});
|
|
});
|
|
|
|
describe('`kind`', () => {
|
|
it('adds a doclet to the list of doclets with that `kind`', () => {
|
|
const doclet = makeDoclet(['@function', '@name foo']);
|
|
|
|
expect(store.docletsByKind.get('function')).toHave(doclet);
|
|
});
|
|
|
|
it('removes a doclet from the list of doclets for a kind when the kind changes', () => {
|
|
const doclet = makeDoclet(['@function', '@name foo']);
|
|
|
|
expect(store.docletsByKind.get('constant')).not.toHave(doclet);
|
|
expect(store.docletsByKind.get('function')).toHave(doclet);
|
|
|
|
doclet.kind = 'constant';
|
|
|
|
expect(store.docletsByKind.get('constant')).toHave(doclet);
|
|
expect(store.docletsByKind.get('function')).not.toHave(doclet);
|
|
});
|
|
|
|
// Doclets should always have a `kind`, so this test is just for completeness.
|
|
it('removes a doclet from the list of doclets for a kind when the doclet loses its kind', () => {
|
|
const doclet = makeDoclet(['@function', '@name foo']);
|
|
|
|
expect(store.docletsByKind.get('function')).toHave(doclet);
|
|
|
|
doclet.kind = 'undefined';
|
|
|
|
expect(store.docletsByKind.get('function')).not.toHave(doclet);
|
|
});
|
|
|
|
it('adds a doclet to the list of global doclets when it gains a `kind` that can be global', () => {
|
|
const doclet = makeDoclet(['@class', '@name foo', '@global']);
|
|
|
|
expect(store.globals).toBeEmptySet();
|
|
|
|
doclet.kind = 'function';
|
|
|
|
expect(store.globals).toHave(doclet);
|
|
});
|
|
|
|
it('removes a doclet from the list of global doclets when its new `kind` cannot be global', () => {
|
|
const doclet = makeDoclet(['@function', '@name foo', '@global']);
|
|
|
|
expect(store.globals).toHave(doclet);
|
|
|
|
doclet.kind = 'class';
|
|
|
|
expect(store.globals).toBeEmptySet();
|
|
});
|
|
|
|
it('does not treat the values of other properties as kinds', () => {
|
|
const doclet = makeDoclet(['@function', '@name foo']);
|
|
|
|
doclet.ignore = true;
|
|
doclet.ignore = 'hello';
|
|
|
|
expect(store.docletsByKind.get('hello')).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe('`listens`', () => {
|
|
it('adds a doclet to the list of listeners when the doclet gains a `listens` value', () => {
|
|
const doclet = makeDoclet(['@class', '@memberof foo', '@name Bar']);
|
|
|
|
expect(store.listenersByListensTo.get('event:baz')).toBeUndefined();
|
|
|
|
doclet.listens = ['event:baz'];
|
|
|
|
expect(store.listenersByListensTo.get('event:baz')).toHave(doclet);
|
|
});
|
|
|
|
it('removes a doclet from the list of listeners when the doclet loses all `listens` values', () => {
|
|
const doclet = makeDoclet(['@class', '@memberof foo', '@name Bar', '@listens event:baz']);
|
|
|
|
expect(store.listenersByListensTo.get('event:baz')).toHave(doclet);
|
|
|
|
doclet.listens.pop();
|
|
|
|
expect(store.listenersByListensTo.get('event:baz')).toBeUndefined();
|
|
});
|
|
|
|
it('removes a doclet from the list of listeners when the doclet loses its `listens` property', () => {
|
|
const doclet = makeDoclet(['@class', '@memberof foo', '@name Bar', '@listens event:baz']);
|
|
|
|
expect(store.listenersByListensTo.get('event:baz')).toHave(doclet);
|
|
|
|
doclet.listens = undefined;
|
|
|
|
expect(store.listenersByListensTo.get('event:baz')).toBeUndefined();
|
|
});
|
|
|
|
it('changes the longname that a doclet listens to when needed', () => {
|
|
const doclet = makeDoclet(['@class', '@memberof foo', '@name Bar', '@listens event:baz']);
|
|
|
|
expect(store.listenersByListensTo.get('event:baz')).toHave(doclet);
|
|
expect(store.listenersByListensTo.get('event:qux')).toBeUndefined();
|
|
|
|
doclet.listens[0] = 'event:qux';
|
|
|
|
expect(store.listenersByListensTo.get('event:baz')).toBeUndefined();
|
|
expect(store.listenersByListensTo.get('event:qux')).toHave(doclet);
|
|
});
|
|
|
|
it('does not treat the values of other properties as `listens` values', () => {
|
|
const doclet = makeDoclet(['@class', '@memberof foo', '@name Bar', '@listens event:baz']);
|
|
|
|
doclet.ignore = true;
|
|
doclet.ignore = 'hello';
|
|
|
|
expect(store.listenersByListensTo.get('hello')).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe('`longname`', () => {
|
|
it('tracks a doclet by its new longname after it changes', () => {
|
|
const doclet = makeDoclet(['@class', '@memberof foo', '@name Bar']);
|
|
|
|
expect(store.docletsByLongname.get('foo.Bar')).toHave(doclet);
|
|
expect(store.docletsByLongname.get('zoo.Bar')).toBeUndefined();
|
|
|
|
doclet.memberof = 'zoo';
|
|
doclet.longname = 'zoo.Bar';
|
|
|
|
expect(store.docletsByLongname.get('foo.Bar')).toBeUndefined();
|
|
expect(store.docletsByLongname.get('zoo.Bar')).toHave(doclet);
|
|
});
|
|
|
|
it('does not treat the values of other properties as longnames', () => {
|
|
const doclet = makeDoclet(['@function', '@name foo']);
|
|
|
|
doclet.undocumented = true;
|
|
|
|
expect(store.allDocletsByLongname.get(true)).toBeUndefined();
|
|
});
|
|
|
|
it('does not track a doclet by longname if it is no longer visible', () => {
|
|
const doclet = makeDoclet(['@class', '@memberof foo', '@name Bar']);
|
|
|
|
expect(store.docletsByLongname.get('foo.Bar')).toHave(doclet);
|
|
|
|
doclet.undocumented = true;
|
|
|
|
expect(store.docletsByLongname.get('foo.Bar')).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe('`memberof`', () => {
|
|
it('tracks a doclet by its new memberof after it changes', () => {
|
|
const doclet = makeDoclet(['@class', '@memberof foo', '@name Bar']);
|
|
|
|
expect(store.docletsByMemberof.get('foo')).toHave(doclet);
|
|
expect(store.docletsByMemberof.get('zoo')).toBeUndefined();
|
|
|
|
doclet.memberof = 'zoo';
|
|
doclet.longname = 'zoo.Bar';
|
|
|
|
expect(store.docletsByMemberof.get('foo')).toBeUndefined();
|
|
expect(store.docletsByMemberof.get('zoo')).toHave(doclet);
|
|
});
|
|
|
|
it('does not treat the values of other properties as `memberof` values', () => {
|
|
const doclet = makeDoclet(['@function', '@name bar', '@memberof foo']);
|
|
|
|
doclet.ignore = true;
|
|
doclet.ignore = 'hello';
|
|
|
|
expect(store.docletsByMemberof.get('hello')).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe('`mixes`', () => {
|
|
it('adds a doclet to the list of doclets that mix in others when the doclet gains a `mixes` value', () => {
|
|
const doclet = makeDoclet(['@class', '@name Foo']);
|
|
|
|
expect(store.docletsWithMixes).not.toHave(doclet);
|
|
|
|
doclet.mixes = ['bar'];
|
|
|
|
expect(store.docletsWithMixes).toHave(doclet);
|
|
});
|
|
|
|
it('removes a doclet from the list of doclets that mix in others when the doclet loses all `mixes` values', () => {
|
|
const doclet = makeDoclet(['@class', '@name Foo', '@mixes bar']);
|
|
|
|
expect(store.docletsWithMixes).toHave(doclet);
|
|
|
|
doclet.mixes.pop();
|
|
|
|
expect(store.docletsWithMixes).not.toHave(doclet);
|
|
});
|
|
|
|
it('removes a doclet from the list of doclets that mix in others when the doclet loses its `mixes` property', () => {
|
|
const doclet = makeDoclet(['@class', '@name Foo', '@mixes bar']);
|
|
|
|
expect(store.docletsWithMixes).toHave(doclet);
|
|
|
|
doclet.mixes = undefined;
|
|
|
|
expect(store.docletsWithMixes).not.toHave(doclet);
|
|
});
|
|
|
|
it('does nothing when a doclet mixed in A, but it now mixes in B instead', () => {
|
|
const doclet = makeDoclet(['@class', '@name Foo', '@mixes bar']);
|
|
|
|
expect(store.docletsWithMixes).toHave(doclet);
|
|
|
|
doclet.mixes[0] = 'baz';
|
|
|
|
expect(store.docletsWithMixes).toContain(doclet);
|
|
});
|
|
});
|
|
|
|
describe('`scope`', () => {
|
|
it('adds a doclet to the list of global doclets when it gains global scope', () => {
|
|
const doclet = makeDoclet(['@function', '@name foo']);
|
|
|
|
expect(store.globals).toBeEmptySet();
|
|
|
|
doclet.scope = 'global';
|
|
|
|
expect(store.globals).toHave(doclet);
|
|
});
|
|
|
|
it('removes a doclet from the list of global doclets when it loses global scope', () => {
|
|
const doclet = makeDoclet(['@function', '@name foo', '@global']);
|
|
|
|
expect(store.globals).toHave(doclet);
|
|
|
|
doclet.scope = 'static';
|
|
|
|
expect(store.globals).toBeEmptySet();
|
|
});
|
|
});
|
|
|
|
describe('`undocumented`', () => {
|
|
it('marks the doclet as unused when added', () => {
|
|
const doclet = makeDoclet(['@class', '@memberof foo', '@name Bar']);
|
|
|
|
expect(store.doclets).toHave(doclet);
|
|
expect(store.unusedDoclets).not.toHave(doclet);
|
|
|
|
doclet.undocumented = true;
|
|
|
|
expect(store.doclets).not.toHave(doclet);
|
|
expect(store.unusedDoclets).toHave(doclet);
|
|
});
|
|
|
|
it('marks the doclet as visible when removed', () => {
|
|
const doclet = makeDoclet(['@class', '@memberof foo', '@name Bar', '@undocumented']);
|
|
|
|
expect(store.doclets).not.toHave(doclet);
|
|
expect(store.unusedDoclets).toHave(doclet);
|
|
|
|
doclet.undocumented = undefined;
|
|
|
|
expect(store.doclets).toHave(doclet);
|
|
expect(store.unusedDoclets).not.toHave(doclet);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('sourcePaths', () => {
|
|
it('is an empty array if there are no visible doclets', () => {
|
|
expect(store.sourcePaths).toBeEmptyArray();
|
|
});
|
|
|
|
it('is a list of source paths if there are visible doclets', () => {
|
|
const metaA = {
|
|
filename: '/Users/carolr/code/foo.js',
|
|
lineno: 1,
|
|
};
|
|
const metaB = {
|
|
filename: '/Users/carolr/code/bar.js',
|
|
lineno: 1,
|
|
};
|
|
|
|
makeDoclet(['@function', '@name baz'], metaA);
|
|
makeDoclet(['@function', '@name qux'], metaB);
|
|
|
|
expect(store.sourcePaths).toEqual([
|
|
'/Users/carolr/code/foo.js',
|
|
'/Users/carolr/code/bar.js',
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe('startListening', () => {
|
|
it('starts listening for events if necessary', () => {
|
|
const foo = makeDoclet(['@class', '@name Foo']);
|
|
const bar = makeDoclet(['@class', '@name Bar']);
|
|
|
|
expect(store.doclets).toHave(foo);
|
|
expect(store.doclets).toHave(bar);
|
|
|
|
// Stop listening for events.
|
|
store.stopListening();
|
|
// Change the doclet for `Foo` so that it's not visible.
|
|
foo.undocumented = true;
|
|
// Start listening for events.
|
|
store.startListening();
|
|
// Change the doclet for `Bar` so that it's not visible.
|
|
bar.undocumented = true;
|
|
|
|
// The doclet store didn't observe the change to `Foo`, but it did observe the change to
|
|
// `Bar`. Therefore, `Foo` should be treated as if it's still visible, and `Bar` shouldn't.
|
|
expect(store.doclets).toHave(foo);
|
|
expect(store.unusedDoclets).not.toHave(foo);
|
|
expect(store.doclets).not.toHave(bar);
|
|
expect(store.unusedDoclets).toHave(bar);
|
|
});
|
|
});
|
|
|
|
describe('stopListening', () => {
|
|
it('stops listening for events if necessary', () => {
|
|
makeDoclet(['@class', '@name Foo']);
|
|
store.stopListening();
|
|
makeDoclet(['@class', '@name Bar']);
|
|
|
|
// The doclet store should know about `Foo`, but not `Bar`.
|
|
expect(store.docletsByLongname.get('Foo').size).toBe(1);
|
|
expect(store.docletsByLongname.get('Bar')).toBeUndefined();
|
|
});
|
|
});
|
|
});
|
|
});
|