From 0ebd36db675f36f7575b169440f2c07306c362c7 Mon Sep 17 00:00:00 2001 From: Remco Haszing Date: Sun, 13 Sep 2015 14:42:29 +0200 Subject: [PATCH 1/3] Restructure utils Previously all was directly assigned to the module.exports object. Now it is somewhat more structured. First properties are listed. Next modules.exports is defined, giving an overview of the module. Last named functions are declared. This provides better stacktraces. --- rules/utils/utils.js | 403 ++++++++++++++++++++++++------------------- 1 file changed, 225 insertions(+), 178 deletions(-) diff --git a/rules/utils/utils.js b/rules/utils/utils.js index 12425125..da950989 100644 --- a/rules/utils/utils.js +++ b/rules/utils/utils.js @@ -1,6 +1,59 @@ 'use strict'; +var scopeProperties = [ + '$id', + '$parent', + '$root', + '$destroy', + '$broadcast', + '$emit', + '$on', + '$applyAsync', + '$apply', + '$evalAsync', + '$eval', + '$digest', + '$watchCollection', + '$watchGroup', + '$watch', + '$new' +]; + + +module.exports = { + // Properties + scopeProperties: scopeProperties, + + // Functions + convertPrefixToRegex: convertPrefixToRegex, + convertStringToRegex: convertStringToRegex, + isTypeOfStatement: isTypeOfStatement, + isToStringStatement: isToStringStatement, + isArrayType: isArrayType, + isFunctionType: isFunctionType, + isIdentifierType: isIdentifierType, + isMemberExpression: isMemberExpression, + isLiteralType: isLiteralType, + isEmptyFunction: isEmptyFunction, + isRegexp: isRegexp, + isStringRegexp: isStringRegexp, + isAngularComponent: isAngularComponent, + isAngularControllerDeclaration: isAngularControllerDeclaration, + isAngularFilterDeclaration: isAngularFilterDeclaration, + isAngularDirectiveDeclaration: isAngularDirectiveDeclaration, + isAngularServiceDeclaration: isAngularServiceDeclaration, + isAngularModuleDeclaration: isAngularModuleDeclaration, + isAngularModuleGetter: isAngularModuleGetter, + isAngularRunSection: isAngularRunSection, + isAngularConfigSection: isAngularConfigSection, + isRouteDefinition: isRouteDefinition, + isUIRouterStateDefinition: isUIRouterStateDefinition, + findIdentiferInScope: findIdentiferInScope, + getControllerDefinition: getControllerDefinition +}; + + // this will recursively grab the callee until it hits an Identifier function getCallingIdentifier(calleeObject) { if (calleeObject.type && calleeObject.type === 'Identifier') { @@ -12,190 +65,184 @@ function getCallingIdentifier(calleeObject) { return null; } -module.exports = { +function convertPrefixToRegex(prefix) { + if (typeof prefix !== 'string') { + return prefix; + } - convertPrefixToRegex: function(prefix) { - if (typeof prefix !== 'string') { - return prefix; - } + if (prefix[0] === '/' && prefix[prefix.length - 1] === '/') { + prefix = prefix.substring(1, prefix.length - 2); + } - if (prefix[0] === '/' && prefix[prefix.length - 1] === '/') { - prefix = prefix.substring(1, prefix.length - 2); - } + return new RegExp(prefix + '.*'); +} - return new RegExp(prefix + '.*'); - }, +function convertStringToRegex(string) { + if (string[0] === '/' && string[string.length - 1] === '/') { + string = string.substring(1, string.length - 2); + } + return new RegExp(string); +} - convertStringToRegex: function(string) { - if (string[0] === '/' && string[string.length - 1] === '/') { - string = string.substring(1, string.length - 2); - } - return new RegExp(string); - }, - - isTypeOfStatement: function(node) { - return node.type === 'Identifier' || (node.type === 'UnaryExpression' && node.operator === 'typeof'); - }, - - isToStringStatement: function(node) { - return node.type === 'CallExpression' && - node.callee.type === 'MemberExpression' && - node.callee.object.type === 'MemberExpression' && - node.callee.object.property.name === 'toString' && - node.callee.property.name === 'call' && - node.callee.object.object.type === 'MemberExpression' && - node.callee.object.object.object.name === 'Object' && - node.callee.object.object.property.name === 'prototype'; - }, - - isArrayType: function(node) { - return node !== undefined && node.type === 'ArrayExpression'; - }, - - isFunctionType: function(node) { - return node !== undefined && node.type === 'FunctionExpression'; - }, - - isIdentifierType: function(node) { - return node !== undefined && node.type === 'Identifier'; - }, - - isMemberExpression: function(node) { - return node !== undefined && node.type === 'MemberExpression'; - }, - - isLiteralType: function(node) { - return node !== undefined && node.type === 'Literal'; - }, - - isEmptyFunction: function(fn) { - return fn.body.body.length === 0; - }, - - isRegexp: function(regexp) { - return toString.call(regexp) === '[object RegExp]'; - }, - - isStringRegexp: function(string) { - return string[0] === '/' && string[string.length - 1] === '/'; - }, - - isAngularComponent: function(node) { - return node.arguments !== undefined && node.arguments.length === 2 && this.isLiteralType(node.arguments[0]) && (this.isIdentifierType(node.arguments[1]) || this.isFunctionType(node.arguments[1]) || this.isArrayType(node.arguments[1])); - }, - - isAngularControllerDeclaration: function(node) { - return this.isAngularComponent(node) && - this.isMemberExpression(node.callee) && - node.callee.property.name === 'controller'; - }, - - isAngularFilterDeclaration: function(node) { - return this.isAngularComponent(node) && - this.isMemberExpression(node.callee) && - node.callee.property.name === 'filter'; - }, - - isAngularDirectiveDeclaration: function(node) { - return this.isAngularComponent(node) && - this.isMemberExpression(node.callee) && - node.callee.property.name === 'directive'; - }, - - isAngularServiceDeclaration: function(node) { - return this.isAngularComponent(node) && - this.isMemberExpression(node.callee) && - node.callee.object.name !== '$provide' && - (node.callee.property.name === 'provider' || - node.callee.property.name === 'service' || - node.callee.property.name === 'factory' || - node.callee.property.name === 'constant' || - node.callee.property.name === 'value'); - }, - - isAngularModuleDeclaration: function(node) { - return this.isAngularComponent(node) && - this.isMemberExpression(node.callee) && - node.callee.property.name === 'module'; - }, - - isAngularModuleGetter: function(node) { - return node.arguments !== undefined && - node.arguments.length > 0 && - this.isLiteralType(node.arguments[0]) && - node.callee.type === 'MemberExpression' && - node.callee.property.name === 'module'; - }, - - isAngularRunSection: function(node) { - return this.isMemberExpression(node.callee) && - node.callee.property.type === 'Identifier' && - node.callee.property.name === 'run' && - (node.callee.object.type === 'Identifier' && - node.callee.object.name !== 'mocha'); - }, - - isAngularConfigSection: function(node) { - return this.isMemberExpression(node.callee) && - node.callee.property.type === 'Identifier' && - node.callee.property.name === 'config'; - }, - - isRouteDefinition: function(node) { - // the route def function is .when(), so when we find that, go up through the chain and make sure - // $routeProvider is the calling object - if (node.callee.property && node.callee.property.name === 'when') { - var callObject = getCallingIdentifier(node.callee.object); - return callObject && callObject.name === '$routeProvider'; - } - return false; - }, - - isUIRouterStateDefinition: function(node) { - // the state def function is .state(), so when we find that, go up through the chain and make sure - // $stateProvider is the calling object - if (node.callee.property && node.callee.property.name === 'state') { - var callObject = getCallingIdentifier(node.callee.object); - return callObject && callObject.name === '$stateProvider'; - } - return false; - }, - - scopeProperties: ['$id', '$parent', '$root', '$destroy', '$broadcast', '$emit', '$on', '$applyAsync', '$apply', - '$evalAsync', '$eval', '$digest', '$watchCollection', '$watchGroup', '$watch', '$new'], - - findIdentiferInScope: function(context, identifier) { - var identifierNode = null; - context.getScope().variables.forEach(function(variable) { - if (variable.name === identifier.name) { - identifierNode = variable.defs[0].node; - if (identifierNode.type === 'VariableDeclarator') { - identifierNode = identifierNode.init; - } - } - }); - return identifierNode; - }, - - getControllerDefinition: function(context, node) { - var controllerArg = node.arguments[1]; - - // Three ways of creating a controller function: function expression, - // variable name that references a function, and an array with a function - // as the last item - if (this.isFunctionType(controllerArg)) { - return controllerArg; - } - if (this.isArrayType(controllerArg)) { - controllerArg = controllerArg.elements[controllerArg.elements.length - 1]; +function isTypeOfStatement(node) { + return node.type === 'Identifier' || (node.type === 'UnaryExpression' && node.operator === 'typeof'); +} + +function isToStringStatement(node) { + return node.type === 'CallExpression' && + node.callee.type === 'MemberExpression' && + node.callee.object.type === 'MemberExpression' && + node.callee.object.property.name === 'toString' && + node.callee.property.name === 'call' && + node.callee.object.object.type === 'MemberExpression' && + node.callee.object.object.object.name === 'Object' && + node.callee.object.object.property.name === 'prototype'; +} + +function isArrayType(node) { + return node !== undefined && node.type === 'ArrayExpression'; +} + +function isFunctionType(node) { + return node !== undefined && node.type === 'FunctionExpression'; +} + +function isIdentifierType(node) { + return node !== undefined && node.type === 'Identifier'; +} + +function isMemberExpression(node) { + return node !== undefined && node.type === 'MemberExpression'; +} + +function isLiteralType(node) { + return node !== undefined && node.type === 'Literal'; +} + +function isEmptyFunction(fn) { + return fn.body.body.length === 0; +} + +function isRegexp(regexp) { + return toString.call(regexp) === '[object RegExp]'; +} + +function isStringRegexp(string) { + return string[0] === '/' && string[string.length - 1] === '/'; +} + +function isAngularComponent(node) { + return node.arguments !== undefined && node.arguments.length === 2 && isLiteralType(node.arguments[0]) && (isIdentifierType(node.arguments[1]) || isFunctionType(node.arguments[1]) || isArrayType(node.arguments[1])); +} + +function isAngularControllerDeclaration(node) { + return isAngularComponent(node) && + isMemberExpression(node.callee) && + node.callee.property.name === 'controller'; +} + +function isAngularFilterDeclaration(node) { + return isAngularComponent(node) && + isMemberExpression(node.callee) && + node.callee.property.name === 'filter'; +} - if (this.isIdentifierType(controllerArg)) { - return this.findIdentiferInScope(context, controllerArg); +function isAngularDirectiveDeclaration(node) { + return isAngularComponent(node) && + isMemberExpression(node.callee) && + node.callee.property.name === 'directive'; +} + +function isAngularServiceDeclaration(node) { + return isAngularComponent(node) && + isMemberExpression(node.callee) && + node.callee.object.name !== '$provide' && + (node.callee.property.name === 'provider' || + node.callee.property.name === 'service' || + node.callee.property.name === 'factory' || + node.callee.property.name === 'constant' || + node.callee.property.name === 'value'); +} + +function isAngularModuleDeclaration(node) { + return isAngularComponent(node) && + isMemberExpression(node.callee) && + node.callee.property.name === 'module'; +} + +function isAngularModuleGetter(node) { + return node.arguments !== undefined && + node.arguments.length > 0 && + isLiteralType(node.arguments[0]) && + node.callee.type === 'MemberExpression' && + node.callee.property.name === 'module'; +} + +function isAngularRunSection(node) { + return isMemberExpression(node.callee) && + node.callee.property.type === 'Identifier' && + node.callee.property.name === 'run' && + (node.callee.object.type === 'Identifier' && + node.callee.object.name !== 'mocha'); +} + +function isAngularConfigSection(node) { + return isMemberExpression(node.callee) && + node.callee.property.type === 'Identifier' && + node.callee.property.name === 'config'; +} + +function isRouteDefinition(node) { + // the route def function is .when(), so when we find that, go up through the chain and make sure + // $routeProvider is the calling object + if (node.callee.property && node.callee.property.name === 'when') { + var callObject = getCallingIdentifier(node.callee.object); + return callObject && callObject.name === '$routeProvider'; + } + return false; +} + +function isUIRouterStateDefinition(node) { + // the state def function is .state(), so when we find that, go up through the chain and make sure + // $stateProvider is the calling object + if (node.callee.property && node.callee.property.name === 'state') { + var callObject = getCallingIdentifier(node.callee.object); + return callObject && callObject.name === '$stateProvider'; + } + return false; +} + +function findIdentiferInScope(context, identifier) { + var identifierNode = null; + context.getScope().variables.forEach(function(variable) { + if (variable.name === identifier.name) { + identifierNode = variable.defs[0].node; + if (identifierNode.type === 'VariableDeclarator') { + identifierNode = identifierNode.init; } - return controllerArg; } - if (this.isIdentifierType(controllerArg)) { - return this.findIdentiferInScope(context, controllerArg); + }); + return identifierNode; +} + +function getControllerDefinition(context, node) { + var controllerArg = node.arguments[1]; + + // Three ways of creating a controller function: function expression, + // variable name that references a function, and an array with a function + // as the last item + if (isFunctionType(controllerArg)) { + return controllerArg; + } + if (isArrayType(controllerArg)) { + controllerArg = controllerArg.elements[controllerArg.elements.length - 1]; + + if (isIdentifierType(controllerArg)) { + return findIdentiferInScope(context, controllerArg); } + return controllerArg; } -}; + if (isIdentifierType(controllerArg)) { + return findIdentiferInScope(context, controllerArg); + } +} From ed9daeec1d6fba482e25e87704a00a2b45be4d85 Mon Sep 17 00:00:00 2001 From: Remco Haszing Date: Sun, 13 Sep 2015 16:28:08 +0200 Subject: [PATCH 2/3] Add documentation for utils --- rules/utils/utils.js | 254 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 252 insertions(+), 2 deletions(-) diff --git a/rules/utils/utils.js b/rules/utils/utils.js index da950989..9d15c572 100644 --- a/rules/utils/utils.js +++ b/rules/utils/utils.js @@ -54,7 +54,11 @@ module.exports = { }; -// this will recursively grab the callee until it hits an Identifier +/** + * Recursively grab the callee until an Identifier is found. + * + * @todo Needs better documentation. + */ function getCallingIdentifier(calleeObject) { if (calleeObject.type && calleeObject.type === 'Identifier') { return calleeObject; @@ -65,6 +69,14 @@ function getCallingIdentifier(calleeObject) { return null; } +/** + * Convert a prefix string to a RegExp. + * + * `'/app/'` → `/app.*\/` + * + * @param {string} prefix + * @returns {RegExp} + */ function convertPrefixToRegex(prefix) { if (typeof prefix !== 'string') { return prefix; @@ -77,6 +89,15 @@ function convertPrefixToRegex(prefix) { return new RegExp(prefix + '.*'); } +/** + * Convert a string to a RegExp. + * + * `'app'` → `/app/` + * `'/app/'` → `/app/` + * + * @param {string} prefix + * @returns {RegExp} + */ function convertStringToRegex(string) { if (string[0] === '/' && string[string.length - 1] === '/') { string = string.substring(1, string.length - 2); @@ -84,10 +105,19 @@ function convertStringToRegex(string) { return new RegExp(string); } +/** + * @todo Missing documentation + */ function isTypeOfStatement(node) { return node.type === 'Identifier' || (node.type === 'UnaryExpression' && node.operator === 'typeof'); } +/** + * @todo Missing documentation + * + * @param {Object} node The node to check. + * @returns {boolean} Whether or not the node is a `toString` statement. + */ function isToStringStatement(node) { return node.type === 'CallExpression' && node.callee.type === 'MemberExpression' && @@ -99,60 +129,183 @@ function isToStringStatement(node) { node.callee.object.object.property.name === 'prototype'; } +/** + * Check whether or not a node is an ArrayExpression. + * + * @param {Object} node The node to check. + * @returns {boolean} Whether or not the node is an ArrayExpression. + */ function isArrayType(node) { return node !== undefined && node.type === 'ArrayExpression'; } +/** + * Check whether or not a node is an FunctionExpression. + * + * @param {Object} node The node to check. + * @returns {boolean} Whether or not the node is an FunctionExpression. + */ function isFunctionType(node) { return node !== undefined && node.type === 'FunctionExpression'; } +/** + * Check whether or not a node is an Identifier. + * + * @param {Object} node The node to check. + * @returns {boolean} Whether or not the node is an Identifier. + */ function isIdentifierType(node) { return node !== undefined && node.type === 'Identifier'; } +/** + * Check whether or not a node is an MemberExpression. + * + * @param {Object} node The node to check. + * @returns {boolean} Whether or not the node is an MemberExpression. + */ function isMemberExpression(node) { return node !== undefined && node.type === 'MemberExpression'; } +/** + * Check whether or not a node is an Literal. + * + * @param {Object} node The node to check. + * @returns {boolean} Whether or not the node is an Literal. + */ function isLiteralType(node) { return node !== undefined && node.type === 'Literal'; } +/** + * Check whether or not a node is an isEmptyFunction. + * + * @param {Object} node The node to check. + * @returns {boolean} Whether or not the node is an isEmptyFunction. + */ function isEmptyFunction(fn) { return fn.body.body.length === 0; } +/** + * Check whether or not an object is a RegExp object. + * + * @param regexp The object for which to check if it is a RegExp object. + * @returns {boolean} Shether or not an object is a RegExp object. + */ function isRegexp(regexp) { return toString.call(regexp) === '[object RegExp]'; } +/** + * Check whether or not a string resembles a regular expression. + * + * A string is considered a regular expression if it starts and ends with `/`. + * + * @param {string} The string to check. + * @returns {boolean} Whether or not a string resembles a regular expression. + */ function isStringRegexp(string) { return string[0] === '/' && string[string.length - 1] === '/'; } +/** + * Check if a CallExpression node somewhat resembles an Angular component. + * + * The following are considered Angular components + * ```js + * app.factory('kittenService', function() {}) + * ^^^^^^^ + * app.factory('kittenService', kittenService) + * ^^^^^^^ + * app.factory('kittenService', []) + * ^^^^^^^ + * asyncFn('value', callback) + * ^^^^^^^ + * ``` + * + * @todo FIXME + * + * @param {Object} node The CallExpression node to check. + * @returns {boolean} Whether or not the node somewhat resembles an Angular component. + */ function isAngularComponent(node) { - return node.arguments !== undefined && node.arguments.length === 2 && isLiteralType(node.arguments[0]) && (isIdentifierType(node.arguments[1]) || isFunctionType(node.arguments[1]) || isArrayType(node.arguments[1])); + return node.arguments !== undefined && + node.arguments.length === 2 && + isLiteralType(node.arguments[0]) && + (isIdentifierType(node.arguments[1]) || + isFunctionType(node.arguments[1]) || + isArrayType(node.arguments[1])); } +/** + * Check whether a CallExpression node defines an Angular controller. + * + * @param {Object} node The CallExpression node to check. + * @returns {boolean} Whether or not the node defines an Angular controller. + */ function isAngularControllerDeclaration(node) { return isAngularComponent(node) && isMemberExpression(node.callee) && node.callee.property.name === 'controller'; } +/** + * Check whether a CallExpression node defines an Angular filter. + * + * @param {Object} node The CallExpression node to check. + * @returns {boolean} Whether or not the node defines an Angular filter. + */ function isAngularFilterDeclaration(node) { return isAngularComponent(node) && isMemberExpression(node.callee) && node.callee.property.name === 'filter'; } +/** + * Check whether a CallExpression node defines an Angular directive. + * + * @param {Object} node The CallExpression node to check. + * @returns {boolean} Whether or not the node defines an Angular directive. + */ function isAngularDirectiveDeclaration(node) { return isAngularComponent(node) && isMemberExpression(node.callee) && node.callee.property.name === 'directive'; } +/** + * Check whether a node defines an Angular service. + * + * The following are considered services + * ```js + * app.provider('kittenServiceProvider', function() {}) + * ^^^^^^^^ + * app.factory('kittenService', function() {}) + * ^^^^^^^ + * app.service('kittenService', function() {}) + * ^^^^^^^ + * app.constant('KITTENS', function() {}) + * ^^^^^^^^ + * app.value('KITTENS', function() {}) + * ^^^^^ + * ``` + * + * The following are not considered services + * ```js + * $provide.factory('kittenService', function() {}) + * app.constant('KITTENS', 'meow') + * app.value('KITTENS', 'purr') + * this.$get = function() {} + * ``` + * + * @todo FIXME + * + * @param {Object} node The CallExpression node to check. + * @returns {boolean} Whether or not the node defines an Angular controller. + */ function isAngularServiceDeclaration(node) { return isAngularComponent(node) && isMemberExpression(node.callee) && @@ -164,12 +317,24 @@ function isAngularServiceDeclaration(node) { node.callee.property.name === 'value'); } +/** + * Check whether a CallExpression node declares an Angular module. + * + * @param {Object} node The CallExpression node to check. + * @returns {boolean} Whether or not the node declares an Angular module. + */ function isAngularModuleDeclaration(node) { return isAngularComponent(node) && isMemberExpression(node.callee) && node.callee.property.name === 'module'; } +/** + * Check whether a CallExpression node gets or declares an Angular module. + * + * @param {Object} node The CallExpression node to check. + * @returns {boolean} Whether or not the node gets or declares an Angular module. + */ function isAngularModuleGetter(node) { return node.arguments !== undefined && node.arguments.length > 0 && @@ -178,6 +343,29 @@ function isAngularModuleGetter(node) { node.callee.property.name === 'module'; } +/** + * Check whether a CallExpression node defines an Angular run function. + * + * The following are considered run functions + * ```js + * app.run() + * ^^^ + * app.run(function() {}) + * ^^^ + * ``` + * + * The following are not considered run functions + * ```js + * angular.module('myApp').run(function() {}) + * angular.module('myApp', []).run(function() {}) + * mocha.run() + * ``` + * + * @todo FIXME + * + * @param {Object} node The CallExpression node to check. + * @returns {boolean} Whether or not the node defines an Angular run function. + */ function isAngularRunSection(node) { return isMemberExpression(node.callee) && node.callee.property.type === 'Identifier' && @@ -186,12 +374,46 @@ function isAngularRunSection(node) { node.callee.object.name !== 'mocha'); } +/** + * Check whether a CallExpression node defines an Angular config function. + * + * The following are considered config functions + * ```js + * app.config() + * ^^^^^^ + * app.config(function() {}) + * ^^^^^^ + * ``` + * + * The following are not considered run functions + * ```js + * angular.module('myApp').config(function() {}) + * angular.module('myApp', []).config(function() {}) + * ``` + * + * @todo FIXME + * + * @param {Object} node The CallExpression node to check. + * @returns {boolean} Whether or not the node defines an Angular config function. + */ function isAngularConfigSection(node) { return isMemberExpression(node.callee) && node.callee.property.type === 'Identifier' && node.callee.property.name === 'config'; } +/** + * Check whether a CallExpression node defines a route using $routeProvider. + * + * The following are considered routes: + * ```js + * $routeProvider.when() + * ^^^^ + * ``` + * + * @param {Object} node The CallExpression node to check. + * @returns {boolean} Whether or not the node defines a route. + */ function isRouteDefinition(node) { // the route def function is .when(), so when we find that, go up through the chain and make sure // $routeProvider is the calling object @@ -202,6 +424,18 @@ function isRouteDefinition(node) { return false; } +/** + * Check whether a CallExpression node defines a state using $stateProvider. + * + * The following are considered states: + * ```js + * $stateProvider.state() + * ^^^^^ + * ``` + * + * @param {Object} node The CallExpression node to check. + * @returns {boolean} Whether or not the node defines a state. + */ function isUIRouterStateDefinition(node) { // the state def function is .state(), so when we find that, go up through the chain and make sure // $stateProvider is the calling object @@ -212,6 +446,14 @@ function isUIRouterStateDefinition(node) { return false; } +/** + * Find an identifier node in the current scope. + * + * @param {Object} context The context to use to get the scope. + * @param {Object} identifier The identifier node to look up. + * + * @returns {Object} The node declaring the identifier. + */ function findIdentiferInScope(context, identifier) { var identifierNode = null; context.getScope().variables.forEach(function(variable) { @@ -225,6 +467,14 @@ function findIdentiferInScope(context, identifier) { return identifierNode; } +/** + * Find the function definition of a controller in the current context. + * + * @param {Object} context The context to use to find the controller declaration. + * @param {Object} node The Angular controller call to look up the declaration for. + * + * @returns {Object} The identifier declaring the controller function. + */ function getControllerDefinition(context, node) { var controllerArg = node.arguments[1]; From 5cd971d9a40a3177243bdcb874c25d66536d9ef7 Mon Sep 17 00:00:00 2001 From: Remco Haszing Date: Thu, 8 Oct 2015 20:05:44 +0200 Subject: [PATCH 3/3] Add tests for some internal functions These tests uncover some issues that need to be looked over. --- gulpfile.js | 2 +- package.json | 3 +- test/utils/utils.js | 98 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 test/utils/utils.js diff --git a/gulpfile.js b/gulpfile.js index c71052ca..5eb7da16 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -18,7 +18,7 @@ gulp.task('test', function(cb) { .pipe(istanbul()) // Covering files .pipe(istanbul.hookRequire()) // Force `require` to return covered files .on('finish', function() { - gulp.src(['test/*.js']) + gulp.src(['test/**/*.js']) .pipe(mocha()) .pipe(istanbul.writeReports()) // Creating the reports after tests runned .on('end', cb); diff --git a/package.json b/package.json index d8fe835f..cbe34ef4 100644 --- a/package.json +++ b/package.json @@ -14,9 +14,10 @@ }, "homepage": "https://github.com/Gillespie59/eslint-plugin-angularjs", "devDependencies": { - "chai": "^3.2.0", + "chai": "^3.3.0", "coveralls": "^2.11.4", "eslint": "^1.3.1", + "espree": "^2.2.5", "gulp": "^3.9.0", "gulp-eslint": "^1.0.0", "gulp-istanbul": "^0.10.0", diff --git a/test/utils/utils.js b/test/utils/utils.js new file mode 100644 index 00000000..c7a77639 --- /dev/null +++ b/test/utils/utils.js @@ -0,0 +1,98 @@ +'use strict'; + +/* eslint-env mocha */ +/* eslint-disable no-unused-expressions */ + +var espree = require('espree'); +var expect = require('chai').expect; + +var utils = require('../../rules/utils/utils'); + + +describe('convertPrefixToRegex', function() { + it('should not handle non-string objects', function() { + var obj = {}; + expect(utils.convertPrefixToRegex(obj) === obj).to.be.true; + }); + + xit('should not convert a string ending and starting with a / to a Regex', function() { + expect(utils.convertPrefixToRegex('/app/'.source)).to.equal('app.*'); + }); + + it('should not convert a regulat string a Regex', function() { + expect(utils.convertPrefixToRegex('app').source).to.equal('app.*'); + }); +}); + +describe('convertStringToRegex', function() { + xit('should not convert a string ending and starting with a / to a Regex', function() { + expect(utils.convertStringToRegex('/app/'.source)).to.equal('app'); + }); + + it('should not convert a regulat string a Regex', function() { + expect(utils.convertStringToRegex('app').source).to.equal('app'); + }); +}); + +describe('isAngularControllerDeclaration', function() { + it('should return true if the function call chained from a module definition declares a controller', function() { + var ast = espree.parse('angular.module("", []).controller("", function() {});'); + expect(utils.isAngularControllerDeclaration(ast.body[0].expression)).to.be.true; + }); + + it('should return true if the function call chained from a module getter declares a controller', function() { + var ast = espree.parse('angular.module("").controller("", function() {});'); + expect(utils.isAngularControllerDeclaration(ast.body[0].expression)).to.be.true; + }); + + xit('should return false if a controller function from some variable is called', function() { + var ast = espree.parse('app.controller("", function() {});'); + expect(utils.isAngularControllerDeclaration(ast.body[0].expression)).to.be.false; + }); + + it('should return true if a referenced angular module declares a controller', function() { + var ast = espree.parse('var app = angular.module("");app.controller("", function() {});'); + expect(utils.isAngularControllerDeclaration(ast.body[1].expression)).to.be.true; + }); + + it('should return false if too few arguments are passed', function() { + var ast = espree.parse('angular.module("").controller("");'); + expect(utils.isAngularControllerDeclaration(ast.body[0].expression)).to.be.false; + }); +}); + +describe('isAngularModuleDeclaration', function() { + it('should return true for an Angular module declaration', function() { + var ast = espree.parse('angular.module("", []);'); + expect(utils.isAngularModuleDeclaration(ast.body[0].expression)).to.be.true; + }); + + it('should return false for an Angular module getter', function() { + var ast = espree.parse('angular.module("");'); + expect(utils.isAngularModuleDeclaration(ast.body[0].expression)).to.be.false; + }); +}); + +describe('isAngularModuleGetter', function() { + xit('should return false for an Angular module declaration', function() { + var ast = espree.parse('angular.module("", []);'); + expect(utils.isAngularModuleGetter(ast.body[0].expression)).to.be.false; + }); + + it('should return true for an Angular module getter', function() { + var ast = espree.parse('angular.module("");'); + expect(utils.isAngularModuleGetter(ast.body[0].expression)).to.be.true; + }); +}); + +describe('isAngularRunSection', function() { + xit('should return true if the call defines a run function', function() { + var ast = espree.parse('angular.module("").run(function() {});'); + expect(utils.isAngularRunSection(ast.body[0].expression)).to.be.true; + }); + + xit('should return false is a run is called on a random object', function() { + var ast = espree.parse('app.run();'); + expect(utils.isAngularRunSection(ast.body[0].expression)).to.be.false; + }); +});