diff --git a/lib/rules/validate-jsdoc.js b/lib/rules/validate-jsdoc.js index 9645329..110fe5e 100644 --- a/lib/rules/validate-jsdoc.js +++ b/lib/rules/validate-jsdoc.js @@ -7,6 +7,7 @@ module.exports = function() {}; module.exports.prototype = { + // load all rules and init them configure: function(options) { assert(typeof options === 'object', 'jsDoc option requires object value'); diff --git a/test/lib/rules/validate-jsdoc/check-redundant-returns.js b/test/lib/rules/validate-jsdoc/check-redundant-returns.js new file mode 100644 index 0000000..7bf7d84 --- /dev/null +++ b/test/lib/rules/validate-jsdoc/check-redundant-returns.js @@ -0,0 +1,52 @@ +describe('rules/validate-jsdoc', function () { + var checker = global.checker({ + additionalRules: ['lib/rules/validate-jsdoc.js'] + }); + + describe('check-redudant-returns', function() { + + checker.rules({checkRedundantReturns: true}); + checker.cases([ + /* jshint ignore:start */ + { + it: 'should report redundant @returns for function', + errors: 3, + code: function () { + /** + * @return {string} + */ + function funcName() { + // dummy + } + + /** + * @returns {String} + */ + function funcName() { + var x = function () { return 1; } + } + + /** + * @returns {String} + */ + function funcName() { + return; + } + } + }, { + it: 'should not report redundant @returns for function', + code: function () { + /** + * @returns {String} + */ + function funcName() { + var x = function () { return 1; } + if (true) { return x; } + } + } + } + /* jshint ignore:end */ + ]); + + }); +}); diff --git a/test/lib/rules/validate-jsdoc/check-return-types.js b/test/lib/rules/validate-jsdoc/check-return-types.js new file mode 100644 index 0000000..d663aa2 --- /dev/null +++ b/test/lib/rules/validate-jsdoc/check-return-types.js @@ -0,0 +1,254 @@ +describe('rules/validate-jsdoc', function () { + var checker = global.checker({ + additionalRules: ['lib/rules/validate-jsdoc.js'] + }); + + describe('check-return-types', function () { + + checker.rules({checkReturnTypes: true}); + checker.cases([ + /* jshint ignore:start */ + { + it: 'should neither throw nor report', + code: function () { + /** + * @return {{a: number, b: string}} + */ + function foo() { + return {}; + } + } + }, { + it: 'should report invalid @returns type in function', + errors: 1, + code: function() { + /** + * @returns {Object} + */ + function funcName() { + return ""; + } + } + }, { + it: 'should not report valid resulting type with object type in method', + code: function() { + Cls.prototype = { + /** + * @return {{bar: number}} + */ + run: function (xxx) { + return {}; + } + }; + } + }, { + it: 'should not report valid resulting type with object type in function', + code: function() { + /** + * @return {Object} + */ + function funcName() { + return new Object(); + } + } + }, { + it: 'should not report comparition jsdoc type to any expression in function', + code: function() { + /** + * @return {Object} + */ + function funcName() { + return Object.create(); + } + /** + * @return {string} + */ + function funcName() { + return makeMyDay("zxc"); + } + } + }, { + it: 'should not report valid resulting array type for function', + code: function() { + /** + * @return {Array} + */ + function funcName() { + return [1, 2]; + } + /** + * @return {Array} + */ + function funcName() { + return new Array("zxc"); + } + } + }, { + it: 'should not report valid resulting regexp type for function', + code: function() { + /** + * @return {RegExp} + */ + function funcName() { + return /[a-z]+/i; + } + /** + * @return {RegExp} + */ + function funcName() { + return new RegExp("[a-z]+", "i"); + } + } + }, { + it: 'should not report valid resulting array. and Object[] for function', + code: function() { + /** + * @return {String[]} + */ + function funcName() { + return ["a", "b", "c"]; + } + /** + * @return {Object[]} + */ + function funcName() { + return [{}, {}]; + } + /** + * @return {Array.} + */ + function funcName() { + return [1, 2, 3]; + } + } + }, { + it: 'should not throw exception on `@returns {null|undefined}` directive. issue #7', + code: function() { + /** + * @return {null} + */ + function funcName() { + return null; + } + /** + * @return {undefined} + */ + function funcName() { + return undefined; + } + /** + * @return {null|undefined} + */ + function funcName(flag) { + if (flag) { + return null; + } + return undefined; + } + } + }, { + it: 'should report on `@returns {null|undefined}` vs (string|number|regexp). issue #7', + errors: 3, + code: function() { + /** + * @return {null|undefined} + */ + function funcName(flag) { + if (flag) { + return /qwe/i; + } else { + return 2; + } + return ""; + } + } + }, { + it: 'should report on `@returns {null|undefined}` vs (array|object). issue #7', + errors: 2, + code: function() { + /** + * @return {null|undefined} + */ + function funcName(flag) { + if (flag) { + return []; + } + return {q: 1}; + } + } + }, { + it: 'should not report on `@returns {string|null}` vs (null). issue #8', + code: function() { + /** + * @return {string|null} + */ + function funcName(flag) { + if (!this.something) { + return null; + } + } + /** + * @return {?string} + */ + function funcName(flag) { + if (!this.something) { + return null; + } + } + } + }, { + it: 'should not report on `@returns {?number}` vs (null|number). issue #8', + code: function() { + /** + * @return {number|null} + */ + function funcName(flag) { + if (!this.something) { + return null; + } + return 3; + } + /** + * @return {?number} + */ + function funcName(flag) { + if (!this.something) { + return null; + } + return 3; + } + } + }, { + it: 'should not report on `@returns {foo.Bar}`. issue #16', + code: function() { + module.exports = { + /** + * @return {foo.Bar} + */ + foo: function () { + return new foo.Bar(); + }, + /** + * @return {foo.Bar.Baz} + */ + bar: function () { + return new foo.Bar.Baz(); + }, + /** + * @return {foo.Bar.Baz|foo.Bar.Baz.Moo} + */ + baz: function (t) { + if (t) { + return new foo.Bar.Baz.Moo(); + } + return new foo.Bar.Baz(); + } + }; + } + } + /* jshint ignore:end */ + ]); + + }); + +}); diff --git a/test/lib/rules/validate-jsdoc/require-return-types.js b/test/lib/rules/validate-jsdoc/require-return-types.js new file mode 100644 index 0000000..9fe1042 --- /dev/null +++ b/test/lib/rules/validate-jsdoc/require-return-types.js @@ -0,0 +1,40 @@ +describe('rules/validate-jsdoc', function () { + var checker = global.checker({ + additionalRules: ['lib/rules/validate-jsdoc.js'] + }); + + describe('require-return-types', function() { + + checker.rules({requireReturnTypes: true}); + checker.cases([ + /* jshint ignore:start */ + { + it: 'should report invalid @returns', + errors: 1, + code: function() { + var x = 1; + /** + * @return + */ + function funcName() { + // dummy + } + } + }, { + it: 'should not report valid jsdoc with object type in method', + code: function() { + Cls.prototype = { + /** + * @return {{bar: number}} + */ + run: function (xxx) { + return {}; + } + }; + } + } + /* jshint ignore:end */ + ]); + + }); +}); diff --git a/test/test.validate-jsdoc-returns.js b/test/test.validate-jsdoc-returns.js deleted file mode 100644 index b28ac5e..0000000 --- a/test/test.validate-jsdoc-returns.js +++ /dev/null @@ -1,384 +0,0 @@ -var Checker = require('jscs/lib/checker'); -var assert = require('assert'); - -describe('rules/validate-jsdoc @returns', function() { - - var checker; - beforeEach(function() { - checker = new Checker(); - checker.registerDefaultRules(); - checker.configure({ additionalRules: ['lib/rules/validate-jsdoc.js'] }); - }); - - describe('require-return-types', function() { - - it('should report invalid @returns', function() { - checker.configure({ jsDoc: { requireReturnTypes: true } }); - assert( - checker.checkString( - 'var x = 1;\n' + - '/**\n' + - ' * @return\n' + - ' */\n' + - 'function funcName() {\n' + - '\n' + - '}' - ).getErrorCount() === 1 - ); - }); - - it('should not report valid jsdoc with object type in method', function() { - checker.configure({ jsDoc: { requireReturnTypes: true } }); - assert( - checker.checkString( - 'Cls.prototype = {\n' + - ' /**\n' + - ' * @return {{bar: number}}\n' + - ' */\n' + - ' run: function (xxx) {\n' + - ' return {};\n' + - ' }\n' + - '};' - ).isEmpty() - ); - }); - - }); - - describe('redudant-returns', function() { - - it('should report redundant @returns for function', function() { - checker.configure({ jsDoc: { checkRedundantReturns: true } }); - assert( - checker.checkString( - '/**\n' + - ' * @return {string}\n' + - ' */\n' + - 'function funcName() {\n' + - '\n' + - '}\n' + - - '/**\n' + - ' * @returns {String}\n' + - ' */\n' + - 'function funcName() {\n' + - 'var x = function () { return 1; }\n' + - '}\n' + - - '/**\n' + - ' * @returns {String}\n' + - ' */\n' + - 'function funcName() {\n' + - 'return;\n' + - '}' - ).getErrorCount() === 3 - ); - }); - - it('should not report redundant @returns for function', function() { - checker.configure({ jsDoc: { checkRedundantReturns: true } }); - assert( - checker.checkString( - '/**\n' + - ' * @returns {String}\n' + - ' */\n' + - 'function funcName() {\n' + - 'var x = function () { return 1; }\n' + - 'if (true) { return x; }\n' + - '}' - ).isEmpty() - ); - }); - - }); - - describe('check-return-types', function() { - - it('should report invalid @returns type in function', function() { - checker.configure({ jsDoc: { checkReturnTypes: true } }); - assert( - checker.checkString( - '/**\n' + - ' * @returns {Object}\n' + - ' */\n' + - 'function funcName() {\n' + - 'return "";\n' + - '}' - ).getErrorCount() === 1 - ); - }); - - it('should not report valid resulting type with object type in method', function() { - checker.configure({ jsDoc: { checkReturnTypes: true } }); - assert( - checker.checkString( - 'Cls.prototype = {\n' + - ' /**\n' + - ' * @return {{bar: number}}\n' + - ' */\n' + - ' run: function (xxx) {\n' + - ' return {};\n' + - ' }\n' + - '};\n' - ).isEmpty() - ); - }); - it('should not report valid resulting type with object type in function', function() { - checker.configure({ jsDoc: { checkReturnTypes: true } }); - assert( - checker.checkString( - '/**\n' + - ' * @return {Object}\n' + - ' */\n' + - 'function funcName() {\n' + - 'return new Object();\n' + - '}' - ).isEmpty() - ); - }); - - it('should not report comparition jsdoc type to any expression in function', function() { - checker.configure({ jsDoc: { checkReturnTypes: true } }); - assert( - checker.checkString( - '/**\n' + - ' * @return {Object}\n' + - ' */\n' + - 'function funcName() {\n' + - 'return Object.create();\n' + - '}\n' + - '/**\n' + - ' * @return {string}\n' + - ' */\n' + - 'function funcName() {\n' + - 'return makeMyDay("zxc");\n' + - '}' - ).isEmpty() - ); - }); - - it('should not report valid resulting array type for function', function() { - checker.configure({ jsDoc: { checkReturnTypes: true } }); - assert( - checker.checkString( - '/**\n' + - ' * @return {Array}\n' + - ' */\n' + - 'function funcName() {\n' + - 'return [1, 2];\n' + - '}\n' + - '/**\n' + - ' * @return {Array}\n' + - ' */\n' + - 'function funcName() {\n' + - 'return new Array("zxc");\n' + - '}' - ).isEmpty() - ); - }); - - it('should not report valid resulting regexp type for function', function() { - checker.configure({ jsDoc: { checkReturnTypes: true } }); - assert( - checker.checkString( - '/**\n' + - ' * @return {RegExp}\n' + - ' */\n' + - 'function funcName() {\n' + - 'return /[a-z]+/i;\n' + - '}\n' + - '/**\n' + - ' * @return {RegExp}\n' + - ' */\n' + - 'function funcName() {\n' + - 'return new RegExp("[a-z]+", "i");\n' + - '}' - ).isEmpty() - ); - }); - - }); - - describe('combined', function() { - - it('should not report valid resulting array. and Object[] for function', function() { - checker.configure({ jsDoc: { requireReturnTypes: true, checkReturnTypes: true } }); - assert( - checker.checkString( - '/**\n' + - ' * @return {String[]}\n' + - ' */\n' + - 'function funcName() {\n' + - 'return ["a", "b", "c"];\n' + - '}\n' + - '/**\n' + - ' * @return {Object[]}\n' + - ' */\n' + - 'function funcName() {\n' + - 'return [{}, {}];\n' + - '}\n' + - '/**\n' + - ' * @return {Array.}\n' + - ' */\n' + - 'function funcName() {\n' + - 'return [1, 2, 3];\n' + - '}' - ).isEmpty() - ); - }); - - }); - - describe('bugfixes', function() { - - it('should not throw exception on `@returns {null|undefined}` directive. issue #7', function() { - - checker.configure({ jsDoc: { requireReturnTypes: true, checkReturnTypes: true } }); - assert( - checker.checkString( - '/**\n' + - ' * @return {null}\n' + - ' */\n' + - 'function funcName() {\n' + - 'return null;\n' + - '}\n' + - '/**\n' + - ' * @return {undefined}\n' + - ' */\n' + - 'function funcName() {\n' + - 'return undefined;\n' + - '}\n' + - '/**\n' + - ' * @return {null|undefined}\n' + - ' */\n' + - 'function funcName(flag) {\n' + - ' if (flag) {\n' + - ' return null;\n' + - ' }\n' + - ' return undefined;\n' + - '}\n' - ).isEmpty() - ); - - }); - - it('should report on `@returns {null|undefined}` vs (string|number|regexp). issue #7', function() { - checker.configure({ jsDoc: { requireReturnTypes: true, checkReturnTypes: true } }); - assert( - checker.checkString( - '/**\n' + - ' * @return {null|undefined}\n' + - ' */\n' + - 'function funcName(flag) {\n' + - ' if (flag) {\n' + - ' return /qwe/i;\n' + - ' } else {\n' + - ' return 2;\n' + - ' }\n' + - ' return "";\n' + - '}\n' - ).getErrorCount() === 3 - ); - }); - - it('should report on `@returns {null|undefined}` vs (array|object). issue #7', function() { - checker.configure({ jsDoc: { requireReturnTypes: true, checkReturnTypes: true } }); - assert( - checker.checkString( - '/**\n' + - ' * @return {null|undefined}\n' + - ' */\n' + - 'function funcName(flag) {\n' + - ' if (flag) {\n' + - ' return [];\n' + - ' }\n' + - ' return {q: 1};\n' + - '}\n' - ).getErrorCount() === 2 - ); - }); - - it('should not report on `@returns {string|null}` vs (null). issue #8', function() { - checker.configure({ jsDoc: { requireReturnTypes: true, checkReturnTypes: true } }); - assert( - checker.checkString( - '/**\n' + - ' * @return {string|null}\n' + - ' */\n' + - 'function funcName(flag) {\n' + - ' if (!this.something) {\n' + - ' return null;\n' + - ' }\n' + - '}\n' + - '/**\n' + - ' * @return {?string}\n' + - ' */\n' + - 'function funcName(flag) {\n' + - ' if (!this.something) {\n' + - ' return null;\n' + - ' }\n' + - '}\n' - ).isEmpty() - ); - }); - - it('should not report on `@returns {?number}` vs (null|number). issue #8', function() { - checker.configure({ jsDoc: { requireReturnTypes: true, checkReturnTypes: true } }); - assert( - checker.checkString( - '/**\n' + - ' * @return {number|null}\n' + - ' */\n' + - 'function funcName(flag) {\n' + - ' if (!this.something) {\n' + - ' return null;\n' + - ' }\n' + - ' return 3;\n' + - '}\n' + - '/**\n' + - ' * @return {?number}\n' + - ' */\n' + - 'function funcName(flag) {\n' + - ' if (!this.something) {\n' + - ' return null;\n' + - ' }\n' + - ' return 3;\n' + - '}\n' - ).isEmpty() - ); - }); - - it('should not report on `@returns {foo.Bar}`. issue #16', function() { - checker.configure({ jsDoc: { requireReturnTypes: true, checkReturnTypes: true } }); - assert( - checker.checkString( - 'module.exports = {\n' + - ' /**\n' + - ' * @return {foo.Bar}\n' + - ' */\n' + - ' foo: function () {\n' + - ' return new foo.Bar();\n' + - ' },\n' + - ' /**\n' + - ' * @return {foo.Bar.Baz}\n' + - ' */\n' + - ' bar: function () {\n' + - ' return new foo.Bar.Baz();\n' + - ' },\n' + - ' /**\n' + - ' * @return {foo.Bar.Baz|foo.Bar.Baz.Moo}\n' + - ' */\n' + - ' baz: function (t) {\n' + - ' if (t) {\n' + - ' return new foo.Bar.Baz.Moo();\n' + - ' }\n' + - ' return new foo.Bar.Baz();\n' + - ' }\n' + - '};\n' - ).isEmpty() - ); - }); - - }); - -});