const { parse } = require('../../lib/index.cjs'); test('description only', () => { const parsed = parse(` /** * Description */`); expect(parsed).toEqual([ { description: 'Description', tags: [], source: [ { number: 1, source: ' /**', tokens: { start: ' ', delimiter: '/**', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 2, source: ' * Description', tokens: { start: ' ', delimiter: '*', postDelimiter: ' ', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: 'Description', end: '', lineEnd: '', }, }, { number: 3, source: ' */', tokens: { start: ' ', delimiter: '', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '*/', lineEnd: '', }, }, ], problems: [], }, ]); }); test('description one-liner', () => { const parsed = parse(` /** Description */ var a`); expect(parsed).toEqual([ { description: 'Description', tags: [], source: [ { number: 1, source: ' /** Description */', tokens: { start: ' ', delimiter: '/**', postDelimiter: ' ', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: 'Description ', end: '*/', lineEnd: '', }, }, ], problems: [], }, ]); }); test('block closed on same line', () => { const parsed = parse(` /** * Description */`); expect(parsed).toEqual([ { description: 'Description', tags: [], source: [ { number: 1, source: ' /**', tokens: { start: ' ', delimiter: '/**', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 2, source: ' * Description */', tokens: { start: ' ', delimiter: '*', postDelimiter: ' ', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: 'Description ', end: '*/', lineEnd: '', }, }, ], problems: [], }, ]); }); test('no mid stars', () => { const parsed = parse(` /** Description */`); expect(parsed).toEqual([ { description: 'Description', tags: [], source: [ { number: 1, source: ' /**', tokens: { start: ' ', delimiter: '/**', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 2, source: ' Description', tokens: { start: ' ', delimiter: '', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: 'Description', end: '', lineEnd: '', }, }, { number: 3, source: ' */', tokens: { start: ' ', delimiter: '', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '*/', lineEnd: '', }, }, ], problems: [], }, ]); }); test('skip surrounding empty lines while preserving line numbers', () => { const parsed = parse(` /** * * * Description first line * * Description second line * */ var a`); expect(parsed).toEqual([ { description: 'Description first line Description second line', tags: [], source: [ { number: 1, source: ' /**', tokens: { start: ' ', delimiter: '/**', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 2, source: ' *', tokens: { start: ' ', delimiter: '*', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 3, source: ' *', tokens: { start: ' ', delimiter: '*', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 4, source: ' * Description first line', tokens: { start: ' ', delimiter: '*', postDelimiter: ' ', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: 'Description first line', end: '', lineEnd: '', }, }, { number: 5, source: ' *', tokens: { start: ' ', delimiter: '*', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 6, source: ' * Description second line', tokens: { start: ' ', delimiter: '*', postDelimiter: ' ', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: 'Description second line', end: '', lineEnd: '', }, }, { number: 7, source: ' *', tokens: { start: ' ', delimiter: '*', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 8, source: ' */', tokens: { start: ' ', delimiter: '', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '*/', lineEnd: '', }, }, ], problems: [], }, ]); }); test('description on the first line', () => { const parsed = parse(` /** Description first line * * Description second line */ var a`); expect(parsed).toEqual([ { description: 'Description first line Description second line', tags: [], source: [ { number: 1, source: ' /** Description first line', tokens: { start: ' ', delimiter: '/**', postDelimiter: ' ', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: 'Description first line', end: '', lineEnd: '', }, }, { number: 2, source: ' *', tokens: { start: ' ', delimiter: '*', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 3, source: ' * Description second line', tokens: { start: ' ', delimiter: '*', postDelimiter: ' ', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: 'Description second line', end: '', lineEnd: '', }, }, { number: 4, source: ' */', tokens: { start: ' ', delimiter: '', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '*/', lineEnd: '', }, }, ], problems: [], }, ]); }); test('skip empty blocks', () => { const parsed = parse(` /** * */ var a`); expect(parsed).toHaveLength(0); }); test('multiple blocks', () => { const parsed = parse(` /** * Description first line */ var a /** * Description second line */ var b`); expect(parsed).toHaveLength(2); expect(parsed).toEqual([ { description: 'Description first line', tags: [], source: [ { number: 1, source: ' /**', tokens: { start: ' ', delimiter: '/**', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 2, source: ' * Description first line', tokens: { start: ' ', delimiter: '*', postDelimiter: ' ', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: 'Description first line', end: '', lineEnd: '', }, }, { number: 3, source: ' */', tokens: { start: ' ', delimiter: '', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '*/', lineEnd: '', }, }, ], problems: [], }, { description: 'Description second line', tags: [], source: [ { number: 6, source: ' /**', tokens: { start: ' ', delimiter: '/**', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 7, source: ' * Description second line', tokens: { start: ' ', delimiter: '*', postDelimiter: ' ', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: 'Description second line', end: '', lineEnd: '', }, }, { number: 8, source: ' */', tokens: { start: ' ', delimiter: '', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '*/', lineEnd: '', }, }, ], problems: [], }, ]); }); test('skip `/* */` blocks', () => { const parsed = parse(` /* * */ var a`); expect(parsed).toHaveLength(0); }); test('skip `/*** */` blocks', () => { const parsed = parse(` /*** * */ var a`); expect(parsed).toHaveLength(0); }); test('tag only one-liner', () => { const parsed = parse(`/** @my-tag */`); const source = [ { number: 0, source: '/** @my-tag */', tokens: { start: '', delimiter: '/**', postDelimiter: ' ', tag: '@my-tag', postTag: ' ', name: '', postName: '', type: '', postType: '', description: '', end: '*/', lineEnd: '', }, }, ]; expect(parsed).toEqual([ { description: '', tags: [ { tag: 'my-tag', name: '', type: '', optional: false, description: '', problems: [], source, }, ], source, problems: [], }, ]); }); test('tag only', () => { const parsed = parse(` /** * * @my-tag */ var a`); const source = [ { number: 1, source: ' /**', tokens: { start: ' ', delimiter: '/**', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 2, source: ' *', tokens: { start: ' ', delimiter: '*', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 3, source: ' * @my-tag', tokens: { start: ' ', delimiter: '*', postDelimiter: ' ', tag: '@my-tag', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 4, source: ' */', tokens: { start: ' ', delimiter: '', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '*/', lineEnd: '', }, }, ]; expect(parsed).toEqual([ { description: '', tags: [ { tag: 'my-tag', name: '', type: '', optional: false, description: '', problems: [], source: source.slice(2), }, ], source, problems: [], }, ]); }); test('tag and type only', () => { const parsed = parse(` /** * * @my-tag {my.type} */ var a`); const source = [ { number: 1, source: ' /**', tokens: { start: ' ', delimiter: '/**', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 2, source: ' *', tokens: { start: ' ', delimiter: '*', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 3, source: ' * @my-tag {my.type}', tokens: { start: ' ', delimiter: '*', postDelimiter: ' ', tag: '@my-tag', postTag: ' ', name: '', postName: '', type: '{my.type}', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 4, source: ' */', tokens: { start: ' ', delimiter: '', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '*/', lineEnd: '', }, }, ]; expect(parsed).toEqual([ { description: '', tags: [ { tag: 'my-tag', name: '', type: 'my.type', optional: false, description: '', problems: [], source: source.slice(2), }, ], source, problems: [], }, ]); }); test('tag and name only', () => { const parsed = parse(` /** * * @my-tag my-name */ var a`); const source = [ { number: 1, source: ' /**', tokens: { start: ' ', delimiter: '/**', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 2, source: ' *', tokens: { start: ' ', delimiter: '*', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 3, source: ' * @my-tag my-name', tokens: { start: ' ', delimiter: '*', postDelimiter: ' ', tag: '@my-tag', postTag: ' ', name: 'my-name', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 4, source: ' */', tokens: { start: ' ', delimiter: '', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '*/', lineEnd: '', }, }, ]; expect(parsed).toEqual([ { description: '', tags: [ { tag: 'my-tag', name: 'my-name', type: '', optional: false, description: '', problems: [], source: source.slice(2), }, ], source, problems: [], }, ]); }); test('tag, type, and name', () => { const parsed = parse(` /** * * @my-tag {my.type} my-name */ var a`); const source = [ { number: 1, source: ' /**', tokens: { start: ' ', delimiter: '/**', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 2, source: ' *', tokens: { start: ' ', delimiter: '*', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 3, source: ' * @my-tag {my.type} my-name', tokens: { start: ' ', delimiter: '*', postDelimiter: ' ', tag: '@my-tag', postTag: ' ', name: 'my-name', postName: '', type: '{my.type}', postType: ' ', description: '', end: '', lineEnd: '', }, }, { number: 4, source: ' */', tokens: { start: ' ', delimiter: '', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '*/', lineEnd: '', }, }, ]; expect(parsed).toEqual([ { description: '', tags: [ { tag: 'my-tag', name: 'my-name', type: 'my.type', optional: false, description: '', problems: [], source: source.slice(2), }, ], source, problems: [], }, ]); }); test('tag, type, name, and description', () => { const parsed = parse(` /** * * @my-tag {my.type} my-name description text */ var a`); const source = [ { number: 1, source: ' /**', tokens: { start: ' ', delimiter: '/**', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 2, source: ' *', tokens: { start: ' ', delimiter: '*', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 3, source: ' * @my-tag {my.type} my-name description text', tokens: { start: ' ', delimiter: '*', postDelimiter: ' ', tag: '@my-tag', postTag: ' ', name: 'my-name', postName: ' ', type: '{my.type}', postType: ' ', description: 'description text', end: '', lineEnd: '', }, }, { number: 4, source: ' */', tokens: { start: ' ', delimiter: '', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '*/', lineEnd: '', }, }, ]; expect(parsed).toEqual([ { description: '', tags: [ { tag: 'my-tag', name: 'my-name', type: 'my.type', optional: false, description: 'description text', problems: [], source: source.slice(2), }, ], source, problems: [], }, ]); }); test('description contains /**', () => { const parsed = parse(` /** * * @my-tag {my.type} my-name description text /** */ var a`); const source = [ { number: 1, source: ' /**', tokens: { start: ' ', delimiter: '/**', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 2, source: ' *', tokens: { start: ' ', delimiter: '*', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 3, source: ' * @my-tag {my.type} my-name description text /**', tokens: { start: ' ', delimiter: '*', postDelimiter: ' ', tag: '@my-tag', postTag: ' ', name: 'my-name', postName: ' ', type: '{my.type}', postType: ' ', description: 'description text /**', end: '', lineEnd: '', }, }, { number: 4, source: ' */', tokens: { start: ' ', delimiter: '', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '*/', lineEnd: '', }, }, ]; expect(parsed).toEqual([ { description: '', tags: [ { tag: 'my-tag', name: 'my-name', type: 'my.type', optional: false, description: 'description text /**', problems: [], source: source.slice(2), }, ], source, problems: [], }, ]); }); test('tag, type, name, and description separated by mixed spaces', () => { const parsed = parse(` /** * * @my-tag\t {my.type}\t my-name\t description text */ var a`); const source = [ { number: 1, source: ' /**', tokens: { start: ' ', delimiter: '/**', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 2, source: ' *', tokens: { start: ' ', delimiter: '*', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 3, source: ' * @my-tag\t {my.type}\t my-name\t description text', tokens: { start: ' ', delimiter: '*', postDelimiter: ' ', tag: '@my-tag', postTag: '\t ', name: 'my-name', postName: '\t ', type: '{my.type}', postType: '\t ', description: 'description text', end: '', lineEnd: '', }, }, { number: 4, source: ' */', tokens: { start: ' ', delimiter: '', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '*/', lineEnd: '', }, }, ]; expect(parsed).toEqual([ { description: '', tags: [ { tag: 'my-tag', name: 'my-name', type: 'my.type', optional: false, description: 'description text', problems: [], source: source.slice(2), }, ], source, problems: [], }, ]); }); test('tag with multiline description', () => { const parsed = parse(` /** * @my-tag {my.type} my-name description line 1 * description line 2 * description line 3 */ var a`); const source = [ { number: 1, source: ' /**', tokens: { start: ' ', delimiter: '/**', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 2, source: ' * @my-tag {my.type} my-name description line 1', tokens: { start: ' ', delimiter: '*', postDelimiter: ' ', tag: '@my-tag', postTag: ' ', name: 'my-name', postName: ' ', type: '{my.type}', postType: ' ', description: 'description line 1', end: '', lineEnd: '', }, }, { number: 3, source: ' * description line 2', tokens: { start: ' ', delimiter: '*', postDelimiter: ' ', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: 'description line 2', end: '', lineEnd: '', }, }, { number: 4, source: ' * description line 3', tokens: { start: ' ', delimiter: '*', postDelimiter: ' ', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: 'description line 3', end: '', lineEnd: '', }, }, { number: 5, source: ' */', tokens: { start: ' ', delimiter: '', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '*/', lineEnd: '', }, }, ]; expect(parsed).toEqual([ { description: '', tags: [ { tag: 'my-tag', name: 'my-name', type: 'my.type', optional: false, description: 'description line 1 description line 2 description line 3', problems: [], source: source.slice(1), }, ], source, problems: [], }, ]); }); test('optional name', () => { const parsed = parse(` /** * * @my-tag {my.type} [my-name] description text */ var a`); const source = [ { number: 1, source: ' /**', tokens: { start: ' ', delimiter: '/**', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 2, source: ' *', tokens: { start: ' ', delimiter: '*', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 3, source: ' * @my-tag {my.type} [my-name] description text', tokens: { start: ' ', delimiter: '*', postDelimiter: ' ', tag: '@my-tag', postTag: ' ', name: '[my-name]', postName: ' ', type: '{my.type}', postType: ' ', description: 'description text', end: '', lineEnd: '', }, }, { number: 4, source: ' */', tokens: { start: ' ', delimiter: '', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '*/', lineEnd: '', }, }, ]; expect(parsed).toEqual([ { description: '', tags: [ { tag: 'my-tag', name: 'my-name', type: 'my.type', optional: true, description: 'description text', problems: [], source: source.slice(2), }, ], source, problems: [], }, ]); }); test('report name errors', () => { const parsed = parse(` /** * * @my-tag {my.type} [my-name description text */ var a`); const source = [ { number: 1, source: ' /**', tokens: { start: ' ', delimiter: '/**', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 2, source: ' *', tokens: { start: ' ', delimiter: '*', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 3, source: ' * @my-tag {my.type} [my-name description text', tokens: { start: ' ', delimiter: '*', postDelimiter: ' ', tag: '@my-tag', postTag: ' ', name: '', postName: '', type: '{my.type}', postType: ' ', description: '[my-name description text', end: '', lineEnd: '', }, }, { number: 4, source: ' */', tokens: { start: ' ', delimiter: '', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '*/', lineEnd: '', }, }, ]; const problems = [ { code: 'spec:name:unpaired-brackets', critical: true, message: 'unpaired brackets', line: 3, }, ]; expect(parsed).toEqual([ { description: '', tags: [ { tag: 'my-tag', name: '', type: 'my.type', optional: false, description: '', source: source.slice(2), problems, }, ], source, problems, }, ]); }); test('misaligned block', () => { const parsed = parse(` /** * @my-tag {my.type} my-name description line 1 description line 2 * description line 3 */ var a`); const source = [ { number: 1, source: ' /**', tokens: { start: ' ', delimiter: '/**', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '', lineEnd: '', }, }, { number: 2, source: '* @my-tag {my.type} my-name description line 1', tokens: { start: '', delimiter: '*', postDelimiter: ' ', tag: '@my-tag', postTag: ' ', name: 'my-name', postName: ' ', type: '{my.type}', postType: ' ', description: 'description line 1', end: '', lineEnd: '', }, }, { number: 3, source: ' description line 2', tokens: { start: ' ', delimiter: '', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: 'description line 2', end: '', lineEnd: '', }, }, { number: 4, source: ' * description line 3', tokens: { start: ' ', delimiter: '*', postDelimiter: ' ', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: 'description line 3', end: '', lineEnd: '', }, }, { number: 5, source: ' */', tokens: { start: ' ', delimiter: '', postDelimiter: '', tag: '', postTag: '', name: '', postName: '', type: '', postType: '', description: '', end: '*/', lineEnd: '', }, }, ]; expect(parsed).toEqual([ { description: '', tags: [ { tag: 'my-tag', name: 'my-name', type: 'my.type', optional: false, description: 'description line 1 description line 2 description line 3', problems: [], source: source.slice(1), }, ], source, problems: [], }, ]); });