2 Copyright (C) 2013 Yusuke Suzuki <utatane.tea@gmail.com>
4 Redistribution and use in source and binary forms, with or without
5 modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
13 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
17 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 /*global require describe it*/
28 var fs = require('fs'),
29 path = require('path'),
30 root = path.join(path.dirname(fs.realpathSync(__filename)), '..'),
31 doctrine = require(root);
34 describe('parse', function () {
35 it('alias', function () {
36 var res = doctrine.parse('/** @alias */', { unwrap: true });
37 res.tags.should.have.length(0);
40 it('alias with name', function () {
41 var res = doctrine.parse('/** @alias aliasName */', { unwrap: true });
42 res.tags.should.have.length(1);
43 res.tags[0].should.have.property('title', 'alias');
44 res.tags[0].should.have.property('name', 'aliasName');
47 it('alias with namepath', function () {
48 var res = doctrine.parse('/** @alias aliasName.OK */', { unwrap: true });
49 res.tags.should.have.length(1);
50 res.tags[0].should.have.property('title', 'alias');
51 res.tags[0].should.have.property('name', 'aliasName.OK');
54 it('const', function () {
55 var res = doctrine.parse('/** @const */', { unwrap: true });
56 res.tags.should.have.length(1);
57 res.tags[0].should.have.property('title', 'const');
60 it('const with name', function () {
61 var res = doctrine.parse('/** @const constname */', { unwrap: true });
62 res.tags.should.have.length(1);
63 res.tags[0].should.have.property('title', 'const');
64 res.tags[0].should.have.property('name', 'constname');
67 it('constant with name', function () {
68 var res = doctrine.parse('/** @constant constname */', { unwrap: true });
69 res.tags.should.have.length(1);
70 res.tags[0].should.have.property('title', 'constant');
71 res.tags[0].should.have.property('name', 'constname');
74 it('const with type and name', function () {
75 var res = doctrine.parse('/** @const {String} constname */', { unwrap: true });
76 res.tags.should.have.length(1);
77 res.tags[0].should.have.property('title', 'const');
78 res.tags[0].should.have.property('name', 'constname');
79 res.tags[0].should.have.property('type');
80 res.tags[0].type.should.eql({
81 type: 'NameExpression',
86 it('constant with type and name', function () {
87 var res = doctrine.parse('/** @constant {String} constname */', { unwrap: true });
88 res.tags.should.have.length(1);
89 res.tags[0].should.have.property('title', 'constant');
90 res.tags[0].should.have.property('name', 'constname');
91 res.tags[0].should.have.property('type');
92 res.tags[0].type.should.eql({
93 type: 'NameExpression',
98 it('const multiple', function () {
99 var res = doctrine.parse("/**@const\n @const*/", { unwrap: true });
100 res.tags.should.have.length(2);
101 res.tags[0].should.have.property('title', 'const');
102 res.tags[1].should.have.property('title', 'const');
105 it('const double', function () {
106 var res = doctrine.parse("/**@const\n @const*/", { unwrap: true });
107 res.tags.should.have.length(2);
108 res.tags[0].should.have.property('title', 'const');
109 res.tags[1].should.have.property('title', 'const');
112 it('const triple', function () {
113 var res = doctrine.parse(
120 ].join('\n'), { unwrap: true });
121 res.tags.should.have.length(3);
122 res.tags[0].should.have.property('title', 'const');
123 res.tags[1].should.have.property('title', 'const');
124 res.tags[2].should.have.property('title', 'const');
127 it('constructor', function () {
128 var res = doctrine.parse('/** @constructor */', { unwrap: true });
129 res.tags.should.have.length(1);
130 res.tags[0].should.have.property('title', 'constructor');
133 it('constructor with type', function () {
134 var res = doctrine.parse('/** @constructor {Object} */', { unwrap: true });
135 res.tags.should.have.length(1);
136 res.tags[0].should.have.property('title', 'constructor');
137 res.tags[0].type.should.eql({
138 type: 'NameExpression',
143 it('constructor with type and name', function () {
144 var res = doctrine.parse('/** @constructor {Object} objName */', { unwrap: true });
145 res.tags.should.have.length(1);
146 res.tags[0].should.have.property('title', 'constructor');
147 res.tags[0].should.have.property('name', 'objName');
148 res.tags[0].type.should.eql({
149 type: 'NameExpression',
154 it('class', function () {
155 var res = doctrine.parse('/** @class */', { unwrap: true });
156 res.tags.should.have.length(1);
157 res.tags[0].should.have.property('title', 'class');
160 it('class with type', function () {
161 var res = doctrine.parse('/** @class {Object} */', { unwrap: true });
162 res.tags.should.have.length(1);
163 res.tags[0].should.have.property('title', 'class');
164 res.tags[0].type.should.eql({
165 type: 'NameExpression',
170 it('class with type and name', function () {
171 var res = doctrine.parse('/** @class {Object} objName */', { unwrap: true });
172 res.tags.should.have.length(1);
173 res.tags[0].should.have.property('title', 'class');
174 res.tags[0].should.have.property('name', 'objName');
175 res.tags[0].type.should.eql({
176 type: 'NameExpression',
181 it('deprecated', function () {
182 var res = doctrine.parse('/** @deprecated */', { unwrap: true });
183 res.tags.should.have.length(1);
184 res.tags[0].should.have.property('title', 'deprecated');
187 it('deprecated', function () {
188 var res = doctrine.parse('/** @deprecated some text here describing why it is deprecated */', { unwrap: true });
189 res.tags.should.have.length(1);
190 res.tags[0].should.have.property('title', 'deprecated');
191 res.tags[0].should.have.property('description', 'some text here describing why it is deprecated');
194 it('func', function () {
195 var res = doctrine.parse('/** @func */', { unwrap: true });
196 res.tags.should.have.length(1);
197 res.tags[0].should.have.property('title', 'func');
200 it('func with name', function () {
201 var res = doctrine.parse('/** @func thingName.func */', { unwrap: true });
202 res.tags.should.have.length(1);
203 res.tags[0].should.have.property('title', 'func');
204 res.tags[0].should.have.property('name', 'thingName.func');
207 it('func with type', function () {
208 var res = doctrine.parse('/** @func {Object} thingName.func */', { unwrap: true });
209 res.tags.should.have.length(0);
210 // func does not accept type
213 it('function', function () {
214 var res = doctrine.parse('/** @function */', { unwrap: true });
215 res.tags.should.have.length(1);
216 res.tags[0].should.have.property('title', 'function');
219 it('function with name', function () {
220 var res = doctrine.parse('/** @function thingName.function */', { unwrap: true });
221 res.tags.should.have.length(1);
222 res.tags[0].should.have.property('title', 'function');
223 res.tags[0].should.have.property('name', 'thingName.function');
226 it('function with type', function () {
227 var res = doctrine.parse('/** @function {Object} thingName.function */', { unwrap: true });
228 res.tags.should.have.length(0);
229 // function does not accept type
232 it('member', function () {
233 var res = doctrine.parse('/** @member */', { unwrap: true });
234 res.tags.should.have.length(1);
235 res.tags[0].should.have.property('title', 'member');
238 it('member with name', function () {
239 var res = doctrine.parse('/** @member thingName.name */', { unwrap: true });
240 res.tags.should.have.length(1);
241 res.tags[0].should.have.property('title', 'member');
242 res.tags[0].should.have.property('name', 'thingName.name');
245 it('member with type', function () {
246 var res = doctrine.parse('/** @member {Object} thingName.name */', { unwrap: true });
247 res.tags.should.have.length(1);
248 res.tags[0].should.have.property('title', 'member');
249 res.tags[0].should.have.property('name', 'thingName.name');
250 res.tags[0].should.have.property('type');
251 res.tags[0].type.should.eql({
252 type: 'NameExpression',
257 it('method', function () {
258 var res = doctrine.parse('/** @method */', { unwrap: true });
259 res.tags.should.have.length(1);
260 res.tags[0].should.have.property('title', 'method');
263 it('method with name', function () {
264 var res = doctrine.parse('/** @method thingName.function */', { unwrap: true });
265 res.tags.should.have.length(1);
266 res.tags[0].should.have.property('title', 'method');
267 res.tags[0].should.have.property('name', 'thingName.function');
270 it('method with type', function () {
271 var res = doctrine.parse('/** @method {Object} thingName.function */', { unwrap: true });
272 res.tags.should.have.length(0);
273 // method does not accept type
276 it('mixes', function () {
277 var res = doctrine.parse('/** @mixes */', { unwrap: true });
278 res.tags.should.have.length(0);
281 it('mixes with name', function () {
282 var res = doctrine.parse('/** @mixes thingName */', { unwrap: true });
283 res.tags.should.have.length(1);
284 res.tags[0].should.have.property('title', 'mixes');
285 res.tags[0].should.have.property('name', 'thingName');
288 it('mixes with namepath', function () {
289 var res = doctrine.parse('/** @mixes thingName.name */', { unwrap: true });
290 res.tags.should.have.length(1);
291 res.tags[0].should.have.property('title', 'mixes');
292 res.tags[0].should.have.property('name', 'thingName.name');
295 it('mixin', function () {
296 var res = doctrine.parse('/** @mixin */', { unwrap: true });
297 res.tags.should.have.length(1);
298 res.tags[0].should.have.property('title', 'mixin');
301 it('mixin with name', function () {
302 var res = doctrine.parse('/** @mixin thingName */', { unwrap: true });
303 res.tags.should.have.length(1);
304 res.tags[0].should.have.property('title', 'mixin');
305 res.tags[0].should.have.property('name', 'thingName');
308 it('mixin with namepath', function () {
309 var res = doctrine.parse('/** @mixin thingName.name */', { unwrap: true });
310 res.tags.should.have.length(1);
311 res.tags[0].should.have.property('title', 'mixin');
312 res.tags[0].should.have.property('name', 'thingName.name');
315 it('module', function () {
316 var res = doctrine.parse('/** @module */', { unwrap: true });
317 res.tags.should.have.length(1);
318 res.tags[0].should.have.property('title', 'module');
321 it('module with name', function () {
322 var res = doctrine.parse('/** @module thingName.name */', { unwrap: true });
323 res.tags.should.have.length(1);
324 res.tags[0].should.have.property('title', 'module');
325 res.tags[0].should.have.property('name', 'thingName.name');
328 it('module with type', function () {
329 var res = doctrine.parse('/** @module {Object} thingName.name */', { unwrap: true });
330 res.tags.should.have.length(1);
331 res.tags[0].should.have.property('title', 'module');
332 res.tags[0].should.have.property('name', 'thingName.name');
333 res.tags[0].should.have.property('type');
334 res.tags[0].type.should.eql({
335 type: 'NameExpression',
340 it('name', function () {
341 var res = doctrine.parse('/** @name thingName.name */', { unwrap: true });
342 res.tags.should.have.length(1);
343 res.tags[0].should.have.property('title', 'name');
344 res.tags[0].should.have.property('name', 'thingName.name');
347 it('name', function () {
348 var res = doctrine.parse('/** @name thingName#name */', { unwrap: true });
349 res.tags.should.have.length(1);
350 res.tags[0].should.have.property('title', 'name');
351 res.tags[0].should.have.property('name', 'thingName#name');
354 it('name', function () {
355 var res = doctrine.parse('/** @name thingName~name */', { unwrap: true });
356 res.tags.should.have.length(1);
357 res.tags[0].should.have.property('title', 'name');
358 res.tags[0].should.have.property('name', 'thingName~name');
361 it('name', function () {
362 var res = doctrine.parse('/** @name {thing} thingName.name */', { unwrap: true });
363 // name does not accept type
364 res.tags.should.have.length(0);
367 it('namespace', function () {
368 var res = doctrine.parse('/** @namespace */', { unwrap: true });
369 res.tags.should.have.length(1);
370 res.tags[0].should.have.property('title', 'namespace');
373 it('namespace with name', function () {
374 var res = doctrine.parse('/** @namespace thingName.name */', { unwrap: true });
375 res.tags.should.have.length(1);
376 res.tags[0].should.have.property('title', 'namespace');
377 res.tags[0].should.have.property('name', 'thingName.name');
380 it('namespace with type', function () {
381 var res = doctrine.parse('/** @namespace {Object} thingName.name */', { unwrap: true });
382 res.tags.should.have.length(1);
383 res.tags[0].should.have.property('title', 'namespace');
384 res.tags[0].should.have.property('name', 'thingName.name');
385 res.tags[0].should.have.property('type');
386 res.tags[0].type.should.eql({
387 type: 'NameExpression',
392 it('param', function () {
393 var res = doctrine.parse(
396 " * @param {String} userName",
398 ].join('\n'), { unwrap: true });
399 res.tags.should.have.length(1);
400 res.tags[0].should.have.property('title', 'param');
401 res.tags[0].should.have.property('name', 'userName');
402 res.tags[0].should.have.property('type');
403 res.tags[0].type.should.eql({
404 type: 'NameExpression',
409 it('param with properties', function () {
410 var res = doctrine.parse(
413 " * @param {String} user.name",
415 ].join('\n'), { unwrap: true });
416 res.tags.should.have.length(1);
417 res.tags[0].should.have.property('title', 'param');
418 res.tags[0].should.have.property('name', 'user.name');
419 res.tags[0].should.have.property('type');
420 res.tags[0].type.should.eql({
421 type: 'NameExpression',
426 it('arg with properties', function () {
427 var res = doctrine.parse(
430 " * @arg {String} user.name",
432 ].join('\n'), { unwrap: true });
433 res.tags.should.have.length(1);
434 res.tags[0].should.have.property('title', 'arg');
435 res.tags[0].should.have.property('name', 'user.name');
436 res.tags[0].should.have.property('type');
437 res.tags[0].type.should.eql({
438 type: 'NameExpression',
443 it('argument with properties', function () {
444 var res = doctrine.parse(
447 " * @argument {String} user.name",
449 ].join('\n'), { unwrap: true });
450 res.tags.should.have.length(1);
451 res.tags[0].should.have.property('title', 'argument');
452 res.tags[0].should.have.property('name', 'user.name');
453 res.tags[0].should.have.property('type');
454 res.tags[0].type.should.eql({
455 type: 'NameExpression',
460 it('param typeless', function () {
461 var res = doctrine.parse(
464 " * @param userName",
466 ].join('\n'), { unwrap: true });
467 res.tags.should.have.length(1);
468 res.tags[0].should.eql({
475 var res = doctrine.parse(
478 " * @param userName Something descriptive",
480 ].join('\n'), { unwrap: true });
481 res.tags.should.have.length(1);
482 res.tags[0].should.eql({
486 description: 'Something descriptive'
489 var res = doctrine.parse(
492 " * @param user.name Something descriptive",
494 ].join('\n'), { unwrap: true });
495 res.tags.should.have.length(1);
496 res.tags[0].should.eql({
500 description: 'Something descriptive'
504 it('param broken', function () {
505 var res = doctrine.parse(
508 " * @param {String} userName",
509 " * @param {String userName",
511 ].join('\n'), { unwrap: true });
512 res.tags.should.have.length(1);
513 res.tags[0].should.have.property('title', 'param');
514 res.tags[0].should.have.property('name', 'userName');
515 res.tags[0].should.have.property('type');
516 res.tags[0].type.should.eql({
517 type: 'NameExpression',
522 it('param record', function () {
523 var res = doctrine.parse(
526 " * @param {{ok:String}} userName",
528 ].join('\n'), { unwrap: true });
529 res.tags.should.have.length(1);
530 res.tags[0].should.have.property('title', 'param');
531 res.tags[0].should.have.property('name', 'userName');
532 res.tags[0].should.have.property('type');
533 res.tags[0].type.should.eql({
539 type: 'NameExpression',
546 it('param record broken', function () {
547 var res = doctrine.parse(
550 " * @param {{ok:String} userName",
552 ].join('\n'), { unwrap: true });
553 res.tags.should.be.empty;
556 it('param multiple lines', function () {
557 var res = doctrine.parse(
560 " * @param {string|",
561 " * number} userName",
564 ].join('\n'), { unwrap: true });
565 res.tags.should.have.length(1);
566 res.tags[0].should.have.property('title', 'param');
567 res.tags[0].should.have.property('name', 'userName');
568 res.tags[0].should.have.property('type');
569 res.tags[0].type.should.eql({
572 type: 'NameExpression',
575 type: 'NameExpression',
581 it('param without braces', function () {
582 var res = doctrine.parse(
585 " * @param string name description",
587 ].join('\n'), { unwrap: true });
588 res.tags.should.have.length(1);
589 res.tags[0].should.have.property('title', 'param');
590 res.tags[0].should.have.property('name', 'string');
591 res.tags[0].should.have.property('type', null);
592 res.tags[0].should.have.property('description', 'name description');
595 it('param w/ hyphen before description', function () {
596 var res = doctrine.parse(
599 " * @param {string} name - description",
601 ].join('\n'), { unwrap: true });
602 res.tags.should.have.length(1);
603 res.tags[0].should.eql({
606 type: 'NameExpression',
610 description: 'description'
614 it('param w/ hyphen + leading space before description', function () {
615 var res = doctrine.parse(
618 " * @param {string} name - description",
620 ].join('\n'), { unwrap: true });
621 res.tags.should.have.length(1);
622 res.tags[0].should.eql({
625 type: 'NameExpression',
629 description: ' description'
633 it('description and param separated by blank line', function () {
634 var res = doctrine.parse(
640 " * @param {string} name description",
642 ].join('\n'), { unwrap: true });
643 res.description.should.eql('Description\nblah blah blah');
644 res.tags.should.have.length(1);
645 res.tags[0].should.have.property('title', 'param');
646 res.tags[0].should.have.property('name', 'name');
647 res.tags[0].should.have.property('type');
648 res.tags[0].type.should.eql({
649 type: 'NameExpression',
652 res.tags[0].should.have.property('description', 'description');
655 it('regular block comment instead of jsdoc-style block comment', function () {
656 var res = doctrine.parse(
662 ].join('\n'), { unwrap: true });
663 res.description.should.eql("Description\nblah blah blah");
666 it('augments', function () {
667 var res = doctrine.parse('/** @augments */', { unwrap: true });
668 res.tags.should.have.length(1);
671 it('augments with name', function () {
672 var res = doctrine.parse('/** @augments ClassName */', { unwrap: true });
673 res.tags.should.have.length(1);
674 res.tags[0].should.have.property('title', 'augments');
675 res.tags[0].should.have.property('name', 'ClassName');
678 it('augments with type', function () {
679 var res = doctrine.parse('/** @augments {ClassName} */', { unwrap: true });
680 res.tags.should.have.length(1);
681 res.tags[0].should.have.property('title', 'augments');
682 res.tags[0].should.have.property('type', {
683 type: 'NameExpression',
688 it('augments with name', function () {
689 var res = doctrine.parse('/** @augments ClassName.OK */', { unwrap: true });
690 res.tags.should.have.length(1);
691 res.tags[0].should.have.property('title', 'augments');
692 res.tags[0].should.have.property('name', 'ClassName.OK');
695 it('extends', function () {
696 var res = doctrine.parse('/** @extends */', { unwrap: true });
697 res.tags.should.have.length(1);
700 it('extends with name', function () {
701 var res = doctrine.parse('/** @extends ClassName */', { unwrap: true });
702 res.tags.should.have.length(1);
703 res.tags[0].should.have.property('title', 'extends');
704 res.tags[0].should.have.property('name', 'ClassName');
707 it('extends with type', function () {
708 var res = doctrine.parse('/** @extends {ClassName} */', { unwrap: true });
709 res.tags.should.have.length(1);
710 res.tags[0].should.have.property('title', 'extends');
711 res.tags[0].should.have.property('type', {
712 type: 'NameExpression',
717 it('extends with namepath', function () {
718 var res = doctrine.parse('/** @extends ClassName.OK */', { unwrap: true });
719 res.tags.should.have.length(1);
720 res.tags[0].should.have.property('title', 'extends');
721 res.tags[0].should.have.property('name', 'ClassName.OK');
724 it('prop', function () {
725 var res = doctrine.parse(
728 " * @prop {string} thingName - does some stuff",
730 ].join('\n'), { unwrap: true });
731 res.tags.should.have.length(1);
732 res.tags[0].should.have.property('title', 'prop');
733 res.tags[0].should.have.property('description', 'does some stuff');
734 res.tags[0].type.should.have.property('name', 'string');
735 res.tags[0].should.have.property('name', 'thingName');
738 it('prop without type', function () {
739 var res = doctrine.parse(
742 " * @prop thingName - does some stuff",
744 ].join('\n'), { unwrap: true });
745 res.tags.should.have.length(0);
749 it('property', function () {
750 var res = doctrine.parse(
753 " * @property {string} thingName - does some stuff",
755 ].join('\n'), { unwrap: true });
756 res.tags.should.have.length(1);
757 res.tags[0].should.have.property('title', 'property');
758 res.tags[0].should.have.property('description', 'does some stuff');
759 res.tags[0].type.should.have.property('name', 'string');
760 res.tags[0].should.have.property('name', 'thingName');
763 it('property without type', function () {
764 var res = doctrine.parse(
767 " * @property thingName - does some stuff",
769 ].join('\n'), { unwrap: true });
770 res.tags.should.have.length(0);
773 it('property with nested name', function () {
774 var res = doctrine.parse(
777 " * @property {string} thingName.name - does some stuff",
779 ].join('\n'), { unwrap: true });
780 res.tags.should.have.length(1);
781 res.tags[0].should.have.property('title', 'property');
782 res.tags[0].should.have.property('description', 'does some stuff');
783 res.tags[0].type.should.have.property('name', 'string');
784 res.tags[0].should.have.property('name', 'thingName.name');
787 it('throws', function () {
788 var res = doctrine.parse(
791 " * @throws {Error} if something goes wrong",
793 ].join('\n'), { unwrap: true });
794 res.tags.should.have.length(1);
795 res.tags[0].should.have.property('title', 'throws');
796 res.tags[0].should.have.property('description', 'if something goes wrong');
797 res.tags[0].type.should.have.property('name', 'Error');
800 it('throws without type', function () {
801 var res = doctrine.parse(
804 " * @throws if something goes wrong",
806 ].join('\n'), { unwrap: true });
807 res.tags.should.have.length(1);
808 res.tags[0].should.have.property('title', 'throws');
809 res.tags[0].should.have.property('description', 'if something goes wrong');
812 it('kind', function () {
813 var res = doctrine.parse('/** @kind class */', { unwrap: true });
814 res.tags.should.have.length(1);
815 res.tags[0].should.have.property('title', 'kind');
816 res.tags[0].should.have.property('kind', 'class');
819 it('kind error', function () {
820 var res = doctrine.parse('/** @kind ng */', { unwrap: true, recoverable: true });
821 res.tags.should.have.length(1);
822 res.tags[0].should.have.property('errors');
823 res.tags[0].errors.should.have.length(1);
824 res.tags[0].errors[0].should.equal('Invalid kind name \'ng\'');
827 it('todo', function () {
828 var res = doctrine.parse('/** @todo Write the documentation */', { unwrap: true });
829 res.tags.should.have.length(1);
830 res.tags[0].should.have.property('title', 'todo');
831 res.tags[0].should.have.property('description', 'Write the documentation');
834 it('summary', function () {
836 var res = doctrine.parse('/** @summary ゆるゆり3期おめでとー */', { unwrap: true });
837 res.tags.should.have.length(1);
838 res.tags[0].should.have.property('title', 'summary');
839 res.tags[0].should.have.property('description', 'ゆるゆり3期おめでとー');
842 it('variation', function () {
844 var res = doctrine.parse('/** @variation 42 */', { unwrap: true });
845 res.tags.should.have.length(1);
846 res.tags[0].should.have.property('title', 'variation');
847 res.tags[0].should.have.property('variation', 42);
850 it('variation error', function () {
852 var res = doctrine.parse('/** @variation Animation */', { unwrap: true, recoverable: true });
853 res.tags.should.have.length(1);
854 res.tags[0].should.have.property('errors');
855 res.tags[0].errors.should.have.length(1);
856 res.tags[0].errors[0].should.equal('Invalid variation \'Animation\'');
859 it('access', function () {
860 var res = doctrine.parse('/** @access public */', { unwrap: true });
861 res.tags.should.have.length(1);
862 res.tags[0].should.have.property('title', 'access');
863 res.tags[0].should.have.property('access', 'public');
866 it('access error', function () {
867 var res = doctrine.parse('/** @access ng */', { unwrap: true, recoverable: true });
868 res.tags.should.have.length(1);
869 res.tags[0].should.have.property('errors');
870 res.tags[0].errors.should.have.length(1);
871 res.tags[0].errors[0].should.equal('Invalid access name \'ng\'');
874 it('public', function () {
875 var res = doctrine.parse('/** @public */', { unwrap: true });
876 res.tags.should.have.length(1);
877 res.tags[0].should.have.property('title', 'public');
880 it('public type and description', function () {
881 var res = doctrine.parse('/** @public {number} ok */', { unwrap: true, recoverable: true });
882 res.tags.should.have.length(1);
883 res.tags[0].should.have.property('title', 'public');
884 res.tags[0].should.have.property('description', 'ok');
885 res.tags[0].should.have.property('type');
886 res.tags[0].type.should.eql({
887 type: 'NameExpression',
892 it('protected', function () {
893 var res = doctrine.parse('/** @protected */', { unwrap: true });
894 res.tags.should.have.length(1);
895 res.tags[0].should.have.property('title', 'protected');
898 it('protected type and description', function () {
899 var res = doctrine.parse('/** @protected {number} ok */', { unwrap: true, recoverable: true });
900 res.tags.should.have.length(1);
901 res.tags[0].should.have.property('title', 'protected');
902 res.tags[0].should.have.property('description', 'ok');
903 res.tags[0].should.have.property('type');
904 res.tags[0].type.should.eql({
905 type: 'NameExpression',
910 it('private', function () {
911 var res = doctrine.parse('/** @private */', { unwrap: true });
912 res.tags.should.have.length(1);
913 res.tags[0].should.have.property('title', 'private');
916 it('private type and description', function () {
917 var res = doctrine.parse('/** @private {number} ok */', { unwrap: true, recoverable: true });
918 res.tags.should.have.length(1);
919 res.tags[0].should.have.property('title', 'private');
920 res.tags[0].should.have.property('description', 'ok');
921 res.tags[0].should.have.property('type');
922 res.tags[0].type.should.eql({
923 type: 'NameExpression',
928 it('readonly', function () {
929 var res = doctrine.parse('/** @readonly */', { unwrap: true });
930 res.tags.should.have.length(1);
931 res.tags[0].should.have.property('title', 'readonly');
934 it('readonly error', function () {
935 var res = doctrine.parse('/** @readonly ng */', { unwrap: true, recoverable: true });
936 res.tags.should.have.length(1);
937 res.tags[0].should.have.property('errors');
938 res.tags[0].errors.should.have.length(1);
939 res.tags[0].errors[0].should.equal('Unknown content \'ng\'');
942 it('requires', function () {
943 var res = doctrine.parse('/** @requires */', { unwrap: true });
944 res.tags.should.have.length(0);
947 it('requires with module name', function () {
948 var res = doctrine.parse('/** @requires name.path */', { unwrap: true });
949 res.tags.should.have.length(1);
950 res.tags[0].should.have.property('title', 'requires');
951 res.tags[0].should.have.property('name', 'name.path');
954 it('global', function () {
955 var res = doctrine.parse('/** @global */', { unwrap: true });
956 res.tags.should.have.length(1);
957 res.tags[0].should.have.property('title', 'global');
960 it('global error', function () {
961 var res = doctrine.parse('/** @global ng */', { unwrap: true, recoverable: true });
962 res.tags.should.have.length(1);
963 res.tags[0].should.have.property('errors');
964 res.tags[0].errors.should.have.length(1);
965 res.tags[0].errors[0].should.equal('Unknown content \'ng\'');
968 it('inner', function () {
969 var res = doctrine.parse('/** @inner */', { unwrap: true });
970 res.tags.should.have.length(1);
971 res.tags[0].should.have.property('title', 'inner');
974 it('inner error', function () {
975 var res = doctrine.parse('/** @inner ng */', { unwrap: true, recoverable: true });
976 res.tags.should.have.length(1);
977 res.tags[0].should.have.property('errors');
978 res.tags[0].errors.should.have.length(1);
979 res.tags[0].errors[0].should.equal('Unknown content \'ng\'');
982 it('instance', function () {
983 var res = doctrine.parse('/** @instance */', { unwrap: true });
984 res.tags.should.have.length(1);
985 res.tags[0].should.have.property('title', 'instance');
988 it('instance error', function () {
989 var res = doctrine.parse('/** @instance ng */', { unwrap: true, recoverable: true });
990 res.tags.should.have.length(1);
991 res.tags[0].should.have.property('errors');
992 res.tags[0].errors.should.have.length(1);
993 res.tags[0].errors[0].should.equal('Unknown content \'ng\'');
996 it('since', function () {
997 var res = doctrine.parse('/** @since 1.2.1 */', { unwrap: true });
998 res.tags.should.have.length(1);
999 res.tags[0].should.have.property('title', 'since');
1000 res.tags[0].should.have.property('description', '1.2.1');
1003 it('static', function () {
1004 var res = doctrine.parse('/** @static */', { unwrap: true });
1005 res.tags.should.have.length(1);
1006 res.tags[0].should.have.property('title', 'static');
1009 it('static error', function () {
1010 var res = doctrine.parse('/** @static ng */', { unwrap: true, recoverable: true });
1011 res.tags.should.have.length(1);
1012 res.tags[0].should.have.property('errors');
1013 res.tags[0].errors.should.have.length(1);
1014 res.tags[0].errors[0].should.equal('Unknown content \'ng\'');
1017 it('this', function () {
1018 var res = doctrine.parse(
1021 " * @this thingName",
1023 ].join('\n'), { unwrap: true });
1024 res.tags.should.have.length(1);
1025 res.tags[0].should.have.property('title', 'this');
1026 res.tags[0].should.have.property('name', 'thingName');
1029 it('this with namepath', function () {
1030 var res = doctrine.parse(
1033 " * @this thingName.name",
1035 ].join('\n'), { unwrap: true });
1036 res.tags.should.have.length(1);
1037 res.tags[0].should.have.property('title', 'this');
1038 res.tags[0].should.have.property('name', 'thingName.name');
1041 it('this error', function () {
1042 var res = doctrine.parse(
1047 ].join('\n'), { unwrap: true, recoverable: true });
1048 res.tags.should.have.length(1);
1049 res.tags[0].should.have.property('title', 'this');
1050 res.tags[0].should.have.property('errors');
1051 res.tags[0].errors.should.have.length(1);
1052 res.tags[0].errors[0].should.equal('Missing or invalid tag name');
1055 it('var', function () {
1056 var res = doctrine.parse('/** @var */', { unwrap: true });
1057 res.tags.should.have.length(1);
1058 res.tags[0].should.have.property('title', 'var');
1061 it('var with name', function () {
1062 var res = doctrine.parse('/** @var thingName.name */', { unwrap: true });
1063 res.tags.should.have.length(1);
1064 res.tags[0].should.have.property('title', 'var');
1065 res.tags[0].should.have.property('name', 'thingName.name');
1068 it('var with type', function () {
1069 var res = doctrine.parse('/** @var {Object} thingName.name */', { unwrap: true });
1070 res.tags.should.have.length(1);
1071 res.tags[0].should.have.property('title', 'var');
1072 res.tags[0].should.have.property('name', 'thingName.name');
1073 res.tags[0].should.have.property('type');
1074 res.tags[0].type.should.eql({
1075 type: 'NameExpression',
1080 it('version', function () {
1081 var res = doctrine.parse('/** @version 1.2.1 */', { unwrap: true });
1082 res.tags.should.have.length(1);
1083 res.tags[0].should.have.property('title', 'version');
1084 res.tags[0].should.have.property('description', '1.2.1');
1087 it('incorrect name', function () {
1088 var res = doctrine.parse('/** @name thingName#%name */', { unwrap: true });
1089 // name does not accept type
1090 res.tags.should.have.length(0);
1099 describe('parseType', function () {
1100 it('union type closure-compiler extended', function () {
1101 var type = doctrine.parseType("string|number");
1105 type: 'NameExpression',
1108 type: 'NameExpression',
1114 it('empty union type', function () {
1115 var type = doctrine.parseType("()");
1122 it('comma last array type', function () {
1123 var type = doctrine.parseType("[string,]");
1127 type: 'NameExpression',
1133 it('array type of all literal', function () {
1134 var type = doctrine.parseType("[*]");
1143 it('array type of nullable literal', function () {
1144 var type = doctrine.parseType("[?]");
1148 type: 'NullableLiteral'
1153 it('comma last record type', function () {
1154 var type = doctrine.parseType("{,}");
1161 it('type application', function () {
1162 var type = doctrine.parseType("Array.<String>");
1164 type: 'TypeApplication',
1166 type: 'NameExpression',
1170 type: 'NameExpression',
1176 it('type application with NullableLiteral', function () {
1177 var type = doctrine.parseType("Array<?>");
1179 type: 'TypeApplication',
1181 type: 'NameExpression',
1185 type: 'NullableLiteral'
1190 it('type application with multiple patterns', function () {
1191 var type = doctrine.parseType("Array.<String, Number>");
1193 type: 'TypeApplication',
1195 type: 'NameExpression',
1199 type: 'NameExpression',
1202 type: 'NameExpression',
1208 it('type application without dot', function () {
1209 var type = doctrine.parseType("Array<String>");
1211 type: 'TypeApplication',
1213 type: 'NameExpression',
1217 type: 'NameExpression',
1223 it('array-style type application', function () {
1224 var type = doctrine.parseType("String[]");
1226 type: 'TypeApplication',
1228 type: 'NameExpression',
1232 type: 'NameExpression',
1238 it('function type simple', function () {
1239 var type = doctrine.parseType("function()");
1241 "type": "FunctionType",
1247 it('function type with name', function () {
1248 var type = doctrine.parseType("function(a)");
1250 "type": "FunctionType",
1253 "type": "NameExpression",
1260 it('function type with name and type', function () {
1261 var type = doctrine.parseType("function(a:b)");
1263 "type": "FunctionType",
1266 "type": "ParameterType",
1269 "type": "NameExpression",
1277 it('function type with optional param', function () {
1278 var type = doctrine.parseType("function(a=)");
1280 "type": "FunctionType",
1283 "type": "OptionalType",
1285 "type": "NameExpression",
1293 it('function type with optional param name and type', function () {
1294 var type = doctrine.parseType("function(a:b=)");
1296 "type": "FunctionType",
1299 "type": "OptionalType",
1301 "type": "ParameterType",
1304 "type": "NameExpression",
1313 it('function type with rest param', function () {
1314 var type = doctrine.parseType("function(...a)");
1316 "type": "FunctionType",
1321 "type": "NameExpression",
1329 it('function type with rest param name and type', function () {
1330 var type = doctrine.parseType("function(...a:b)");
1332 "type": "FunctionType",
1337 "type": "ParameterType",
1340 "type": "NameExpression",
1350 it('function type with optional rest param', function () {
1351 var type = doctrine.parseType("function(...a=)");
1353 "type": "FunctionType",
1358 "type": "OptionalType",
1360 "type": "NameExpression",
1369 it('function type with optional rest param name and type', function () {
1370 var type = doctrine.parseType("function(...a:b=)");
1372 "type": "FunctionType",
1377 "type": "OptionalType",
1379 "type": "ParameterType",
1382 "type": "NameExpression",
1392 it('string value in type', function () {
1395 type = doctrine.parseType("{'ok':String}");
1400 "type": "FieldType",
1403 "type": "NameExpression"
1407 "type": "RecordType"
1410 type = doctrine.parseType('{"\\r\\n\\t\\u2028\\x20\\u20\\b\\f\\v\\\r\n\\\n\\0\\07\\012\\o":String}');
1414 "key": "\r\n\t\u2028\x20u20\b\f\v\0\u0007\u000ao",
1415 "type": "FieldType",
1418 "type": "NameExpression"
1422 "type": "RecordType"
1425 doctrine.parseType.bind(doctrine, "{'ok\":String}").should.throw('unexpected quote');
1426 doctrine.parseType.bind(doctrine, "{'o\n':String}").should.throw('unexpected quote');
1429 it('number value in type', function () {
1432 type = doctrine.parseType("{20:String}");
1437 "type": "FieldType",
1440 "type": "NameExpression"
1444 "type": "RecordType"
1447 type = doctrine.parseType("{.2:String, 30:Number, 0x20:String}");
1452 "type": "FieldType",
1455 "type": "NameExpression"
1460 "type": "FieldType",
1463 "type": "NameExpression"
1468 "type": "FieldType",
1471 "type": "NameExpression"
1475 "type": "RecordType"
1479 type = doctrine.parseType("{0X2:String, 0:Number, 100e200:String, 10e-20:Number}");
1484 "type": "FieldType",
1487 "type": "NameExpression"
1492 "type": "FieldType",
1495 "type": "NameExpression"
1500 "type": "FieldType",
1503 "type": "NameExpression"
1508 "type": "FieldType",
1511 "type": "NameExpression"
1515 "type": "RecordType"
1519 doctrine.parseType.bind(doctrine, "{0x:String}").should.throw('unexpected token');
1520 doctrine.parseType.bind(doctrine, "{0x").should.throw('unexpected token');
1521 doctrine.parseType.bind(doctrine, "{0xd").should.throw('unexpected token');
1522 doctrine.parseType.bind(doctrine, "{0x2_:").should.throw('unexpected token');
1523 doctrine.parseType.bind(doctrine, "{021:").should.throw('unexpected token');
1524 doctrine.parseType.bind(doctrine, "{021_:").should.throw('unexpected token');
1525 doctrine.parseType.bind(doctrine, "{021").should.throw('unexpected token');
1526 doctrine.parseType.bind(doctrine, "{08").should.throw('unexpected token');
1527 doctrine.parseType.bind(doctrine, "{0y").should.throw('unexpected token');
1528 doctrine.parseType.bind(doctrine, "{0").should.throw('unexpected token');
1529 doctrine.parseType.bind(doctrine, "{100e2").should.throw('unexpected token');
1530 doctrine.parseType.bind(doctrine, "{100e-2").should.throw('unexpected token');
1531 doctrine.parseType.bind(doctrine, "{100e-200:").should.throw('unexpected token');
1532 doctrine.parseType.bind(doctrine, "{100e:").should.throw('unexpected token');
1533 doctrine.parseType.bind(doctrine, "function(number=, string)").should.throw('not reach to EOF');
1536 it('dotted type', function () {
1538 type = doctrine.parseType("Cocoa.Cappuccino");
1540 "name": "Cocoa.Cappuccino",
1541 "type": "NameExpression"
1545 it('rest array type', function () {
1547 type = doctrine.parseType("[string,...string]");
1552 "type": "NameExpression"
1557 "type": "NameExpression"
1566 it ('nullable type', function () {
1568 type = doctrine.parseType("string?");
1572 "type": "NameExpression"
1575 "type": "NullableType"
1579 it ('non-nullable type', function () {
1581 type = doctrine.parseType("string!");
1585 "type": "NameExpression"
1588 "type": "NonNullableType"
1592 it ('toplevel multiple pipe type', function () {
1594 type = doctrine.parseType("string|number|Test");
1599 "type": "NameExpression"
1603 "type": "NameExpression"
1607 "type": "NameExpression"
1614 it('illegal tokens', function () {
1615 doctrine.parseType.bind(doctrine, ".").should.throw('unexpected token');
1616 doctrine.parseType.bind(doctrine, ".d").should.throw('unexpected token');
1617 doctrine.parseType.bind(doctrine, "(").should.throw('unexpected token');
1618 doctrine.parseType.bind(doctrine, "Test.").should.throw('unexpected token');
1622 describe('parseParamType', function () {
1623 it('question', function () {
1624 var type = doctrine.parseParamType("?");
1626 type: 'NullableLiteral'
1630 it('question option', function () {
1631 var type = doctrine.parseParamType("?=");
1633 type: 'OptionalType',
1635 type: 'NullableLiteral'
1640 it('function option parameters former', function () {
1641 var type = doctrine.parseParamType("function(?, number)");
1643 type: 'FunctionType',
1645 type: 'NullableLiteral'
1647 type: 'NameExpression',
1654 it('function option parameters latter', function () {
1655 var type = doctrine.parseParamType("function(number, ?)");
1657 type: 'FunctionType',
1659 type: 'NameExpression',
1662 type: 'NullableLiteral'
1668 it('function type union', function () {
1669 var type = doctrine.parseParamType("function(): ?|number");
1673 type: 'FunctionType',
1676 type: 'NullableLiteral'
1679 type: 'NameExpression',
1686 describe('invalid', function () {
1687 it('empty union pipe', function () {
1688 doctrine.parseType.bind(doctrine, "(|)").should.throw();
1689 doctrine.parseType.bind(doctrine, "(string|)").should.throw();
1690 doctrine.parseType.bind(doctrine, "(string||)").should.throw();
1693 it('comma only array type', function () {
1694 doctrine.parseType.bind(doctrine, "[,]").should.throw();
1697 it('comma only record type', function () {
1698 doctrine.parseType.bind(doctrine, "{,,}").should.throw();
1701 it('incorrect bracket', function () {
1702 doctrine.parseParamType.bind(doctrine, "int[").should.throw();
1706 describe('tags option', function() {
1707 it ('only param', function() {
1708 var res = doctrine.parse(
1712 " * @param {String} y",
1714 ].join('\n'), { tags: ['param'], unwrap:true });
1715 res.tags.should.have.length(1);
1716 res.tags[0].should.have.property('title', 'param');
1717 res.tags[0].should.have.property('name', 'y');
1720 it ('param and type', function() {
1721 var res = doctrine.parse(
1725 " * @param {String} y",
1726 " * @type {String} ",
1728 ].join('\n'), { tags: ['param', 'type'], unwrap:true });
1729 res.tags.should.have.length(2);
1730 res.tags[0].should.have.property('title', 'param');
1731 res.tags[0].should.have.property('name', 'y');
1732 res.tags[1].should.have.property('title', 'type');
1733 res.tags[1].should.have.property('type');
1734 res.tags[1].type.should.have.property('name', 'String');
1739 describe('invalid tags', function() {
1740 it ('bad tag 1', function() {
1741 doctrine.parse.bind(doctrine,
1744 " * @param {String} hucairz",
1746 ].join('\n'), { tags: 1, unwrap:true }).should.throw();
1749 it ('bad tag 2', function() {
1750 doctrine.parse.bind(doctrine,
1753 " * @param {String} hucairz",
1755 ].join('\n'), { tags: ['a', 1], unwrap:true }).should.throw();
1759 describe('optional params', function() {
1761 // should fail since sloppy option not set
1762 it('failure 0', function() {
1764 ["/**", " * @param {String} [val]", " */"].join('\n'), {
1772 it('failure 1', function() {
1774 ["/**", " * @param [val", " */"].join('\n'), {
1775 unwrap: true, sloppy: true
1782 it('success 1', function() {
1784 ["/**", " * @param {String} [val]", " */"].join('\n'), {
1785 unwrap: true, sloppy: true
1790 "description": null,
1792 "type": "OptionalType",
1794 "type": "NameExpression",
1802 it('success 2', function() {
1804 ["/**", " * @param {String=} val", " */"].join('\n'), {
1805 unwrap: true, sloppy: true
1810 "description": null,
1812 "type": "OptionalType",
1814 "type": "NameExpression",
1823 it('success 3', function() {
1825 ["/**", " * @param {String=} [val=abc] some description", " */"].join('\n'),
1826 { unwrap: true, sloppy: true}
1831 "description": "some description",
1833 "type": "OptionalType",
1835 "type": "NameExpression",
1845 it('line numbers', function() {
1846 var res = doctrine.parse(
1849 " * @param {string} foo",
1850 " * @returns {string}",
1853 " * f('blah'); // => undefined",
1856 { unwrap: true, lineNumbers: true }
1859 res.tags[0].should.have.property('lineNumber', 1);
1860 res.tags[1].should.have.property('lineNumber', 2);
1861 res.tags[2].should.have.property('lineNumber', 4);
1864 it('should handle \\r\\n line endings correctly', function() {
1865 var res = doctrine.parse(
1868 " * @param {string} foo",
1869 " * @returns {string}",
1872 " * f('blah'); // => undefined",
1875 { unwrap: true, lineNumbers: true }
1878 res.tags[0].should.have.property('lineNumber', 1);
1879 res.tags[1].should.have.property('lineNumber', 2);
1880 res.tags[2].should.have.property('lineNumber', 4);
1884 describe('recovery tests', function() {
1885 it ('params 2', function () {
1886 var res = doctrine.parse(
1889 "@param {string} f2"
1890 ].join('\n'), { recoverable: true });
1892 // ensure both parameters are OK
1893 res.tags.should.have.length(2);
1894 res.tags[0].should.have.property('title', 'param');
1895 res.tags[0].should.have.property('type', null);
1896 res.tags[0].should.have.property('name', 'f');
1898 res.tags[1].should.have.property('title', 'param');
1899 res.tags[1].should.have.property('type');
1900 res.tags[1].type.should.have.property('name', 'string');
1901 res.tags[1].type.should.have.property('type', 'NameExpression');
1902 res.tags[1].should.have.property('name', 'f2');
1905 it ('params 2', function () {
1906 var res = doctrine.parse(
1909 "@param {string} f2"
1910 ].join('\n'), { recoverable: true });
1912 // ensure first parameter is OK even with invalid type name
1913 res.tags.should.have.length(2);
1914 res.tags[0].should.have.property('title', 'param');
1915 res.tags[0].should.have.property('type', null);
1916 res.tags[0].should.have.property('name', 'string');
1917 res.tags[0].should.have.property('description', 'f');
1919 res.tags[1].should.have.property('title', 'param');
1920 res.tags[1].should.have.property('type');
1921 res.tags[1].type.should.have.property('name', 'string');
1922 res.tags[1].type.should.have.property('type', 'NameExpression');
1923 res.tags[1].should.have.property('name', 'f2');
1926 it ('return 1', function() {
1927 var res = doctrine.parse(
1930 ].join('\n'), { recoverable: true });
1932 // return tag should exist
1933 res.tags.should.have.length(1);
1934 res.tags[0].should.have.property('title', 'returns');
1935 res.tags[0].should.have.property('type', null);
1937 it ('return 2', function() {
1938 var res = doctrine.parse(
1941 "@param {string} f2"
1942 ].join('\n'), { recoverable: true });
1944 // return tag should exist as well as next tag
1945 res.tags.should.have.length(2);
1946 res.tags[0].should.have.property('title', 'returns');
1947 res.tags[0].should.have.property('type', null);
1949 res.tags[1].should.have.property('title', 'param');
1950 res.tags[1].should.have.property('type');
1951 res.tags[1].type.should.have.property('name', 'string');
1952 res.tags[1].type.should.have.property('type', 'NameExpression');
1953 res.tags[1].should.have.property('name', 'f2');
1956 it ('extra @ 1', function() {
1957 var res = doctrine.parse(
1961 "@param {string} f2"
1962 ].join('\n'), { recoverable: true });
1964 // empty tag name shouldn't affect subsequent tags
1965 res.tags.should.have.length(3);
1966 res.tags[0].should.have.property('title', '');
1967 res.tags[0].should.not.have.property('type');
1969 res.tags[1].should.have.property('title', 'returns');
1970 res.tags[1].should.have.property('type', null);
1972 res.tags[2].should.have.property('title', 'param');
1973 res.tags[2].should.have.property('type');
1974 res.tags[2].type.should.have.property('name', 'string');
1975 res.tags[2].type.should.have.property('type', 'NameExpression');
1976 res.tags[2].should.have.property('name', 'f2');
1979 it ('extra @ 2', function() {
1980 var res = doctrine.parse(
1983 "@param {string} f2"
1984 ].join('\n'), { recoverable: true });
1986 // empty tag name shouldn't affect subsequent tags
1987 res.tags.should.have.length(2);
1988 res.tags[0].should.have.property('title', '');
1989 res.tags[0].should.not.have.property('type');
1990 res.tags[0].should.not.have.property('name');
1991 res.tags[0].should.have.property('description', 'invalid name');
1993 res.tags[1].should.have.property('title', 'param');
1994 res.tags[1].should.have.property('type');
1995 res.tags[1].type.should.have.property('name', 'string');
1996 res.tags[1].type.should.have.property('type', 'NameExpression');
1997 res.tags[1].should.have.property('name', 'f2');
2000 it ('invalid tag 1', function() {
2001 var res = doctrine.parse(
2003 "@111 invalid name",
2004 "@param {string} f2"
2005 ].join('\n'), { recoverable: true });
2007 // invalid tag name shouldn't affect subsequent tags
2008 res.tags.should.have.length(2);
2009 res.tags[0].should.have.property('title', '111');
2010 res.tags[0].should.not.have.property('type');
2011 res.tags[0].should.not.have.property('name');
2012 res.tags[0].should.have.property('description', 'invalid name');
2014 res.tags[1].should.have.property('title', 'param');
2015 res.tags[1].should.have.property('type');
2016 res.tags[1].type.should.have.property('name', 'string');
2017 res.tags[1].type.should.have.property('type', 'NameExpression');
2018 res.tags[1].should.have.property('name', 'f2');
2021 it ('invalid tag 1', function() {
2022 var res = doctrine.parse(
2025 "@param {string} f2"
2026 ].join('\n'), { recoverable: true });
2028 // invalid tag name shouldn't affect subsequent tags
2029 res.tags.should.have.length(2);
2030 res.tags[0].should.have.property('title', '111');
2031 res.tags[0].should.not.have.property('type');
2032 res.tags[0].should.not.have.property('name');
2033 res.tags[0].should.have.property('description', null);
2035 res.tags[1].should.have.property('title', 'param');
2036 res.tags[1].should.have.property('type');
2037 res.tags[1].type.should.have.property('name', 'string');
2038 res.tags[1].type.should.have.property('type', 'NameExpression');
2039 res.tags[1].should.have.property('name', 'f2');
2042 it ('should not crash on bad type in @param without name', function() {
2043 var res = doctrine.parse("@param {Function(DOMNode)}", { recoverable: true });
2044 res.tags.should.have.length(1);
2045 res.tags[0].should.eql({
2046 "description": null,
2049 "Missing or invalid tag name"
2057 it ('should not crash on bad type in @param in sloppy mode', function() {
2058 var res = doctrine.parse("@param {int[} [x]", { sloppy: true, recoverable: true });
2059 res.tags.should.have.length(1);
2060 res.tags[0].should.eql({
2061 "description": null,
2063 "expected an array-style type declaration (int[])"
2072 describe('exported Syntax', function() {
2073 it ('members', function () {
2074 doctrine.Syntax.should.eql({
2075 NullableLiteral: 'NullableLiteral',
2076 AllLiteral: 'AllLiteral',
2077 NullLiteral: 'NullLiteral',
2078 UndefinedLiteral: 'UndefinedLiteral',
2079 VoidLiteral: 'VoidLiteral',
2080 UnionType: 'UnionType',
2081 ArrayType: 'ArrayType',
2082 RecordType: 'RecordType',
2083 FieldType: 'FieldType',
2084 FunctionType: 'FunctionType',
2085 ParameterType: 'ParameterType',
2086 RestType: 'RestType',
2087 NonNullableType: 'NonNullableType',
2088 OptionalType: 'OptionalType',
2089 NullableType: 'NullableType',
2090 NameExpression: 'NameExpression',
2091 TypeApplication: 'TypeApplication'
2096 describe('@ mark contained descriptions', function () {
2097 it ('comment description #10', function () {
2101 ' * Prevents the default action. It is equivalent to',
2102 ' * {@code e.preventDefault()}, but can be used as the callback argument of',
2103 ' * {@link goog.events.listen} without declaring another function.',
2104 ' * @param {!goog.events.Event} e An event.',
2107 { unwrap: true, sloppy: true }).should.eql({
2108 'description': 'Prevents the default action. It is equivalent to\n{@code e.preventDefault()}, but can be used as the callback argument of\n{@link goog.events.listen} without declaring another function.',
2111 'description': 'An event.',
2113 'type': 'NonNullableType',
2115 'type': 'NameExpression',
2116 'name': 'goog.events.Event'
2125 it ('tag description', function () {
2129 ' * Prevents the default action. It is equivalent to',
2130 ' * @param {!goog.events.Event} e An event.',
2131 ' * {@code e.preventDefault()}, but can be used as the callback argument of',
2132 ' * {@link goog.events.listen} without declaring another function.',
2135 { unwrap: true, sloppy: true }).should.eql({
2136 'description': 'Prevents the default action. It is equivalent to',
2139 'description': 'An event.\n{@code e.preventDefault()}, but can be used as the callback argument of\n{@link goog.events.listen} without declaring another function.',
2141 'type': 'NonNullableType',
2143 'type': 'NameExpression',
2144 'name': 'goog.events.Event'
2154 describe('function', function () {
2155 it ('recognize "function" type', function () {
2156 var res = doctrine.parse(
2158 "@param {function} foo description",
2160 res.tags.should.have.length(1);
2161 res.tags[0].should.have.property('title', 'param');
2162 res.tags[0].should.have.property('type');
2163 res.tags[0].type.should.eql({
2165 "type": "NameExpression"
2167 res.tags[0].should.have.property('name', 'foo');
2168 res.tags[0].should.have.property('description', 'description');
2172 describe('tagged namepaths', function () {
2173 it ('recognize module:', function () {
2174 var res = doctrine.parse(
2176 "@alias module:Foo.bar"
2178 res.tags.should.have.length(1);
2179 res.tags[0].should.have.property('title', 'alias');
2180 res.tags[0].should.have.property('name', 'module:Foo.bar');
2181 res.tags[0].should.have.property('description', null);
2184 it ('recognize external:', function () {
2185 var res = doctrine.parse(
2187 "@param {external:Foo.bar} baz description"
2189 res.tags.should.have.length(1);
2190 res.tags[0].should.have.property('title', 'param');
2191 res.tags[0].type.should.eql({
2192 "name": "external:Foo.bar",
2193 "type": "NameExpression"
2195 res.tags[0].should.have.property('name', 'baz');
2196 res.tags[0].should.have.property('description', 'description');
2199 it ('recognize event:', function () {
2200 var res = doctrine.parse(
2202 "@function event:Foo.bar"
2204 res.tags.should.have.length(1);
2205 res.tags[0].should.have.property('title', 'function');
2206 res.tags[0].should.have.property('name', 'event:Foo.bar');
2207 res.tags[0].should.have.property('description', null);
2210 it ('invalid bogus:', function () {
2211 var res = doctrine.parse(
2213 "@method bogus:Foo.bar"
2215 res.tags.should.have.length(0);
2219 /* vim: set sw=4 ts=4 et tw=80 : */