Repository: lo1tuma/eslint-plugin-mocha (ts)

Issue:
Rule proposal: add "valid-suite-description" rule
Similar to the existing [valid-test-description](https://github.com/lo1tuma/eslint-plugin-mocha/blob/master/lib/rules/valid-test-description.js) rule, but to match against the suite descriptions (`describe` or `context` or `suite`). Thanks.

Pull request description:
feat(rules): add 'valid-suite-description' rule
Should close #69. Thanks.

Patch:
diff --git a/docs/rules/README.md b/docs/rules/README.md
index cfc7180..e10ecda 100644
--- a/docs/rules/README.md
+++ b/docs/rules/README.md
@@ -7,3 +7,4 @@
 * [no-synchronous-tests](no-synchronous-tests.md) - disallow synchronous tests
 * [no-global-tests](no-global-tests.md) - disallow global tests
 * [valid-test-description](valid-test-description.md) - match test descriptions against a pre-configured regular expression
+* [valid-suite-description](valid-suite-description.md) - match suite descriptions against a pre-configured regular expression
diff --git a/docs/rules/valid-suite-description.md b/docs/rules/valid-suite-description.md
new file mode 100644
index 0000000..283e140
--- /dev/null
+++ b/docs/rules/valid-suite-description.md
@@ -0,0 +1,51 @@
+# Match suite descriptions against a pre-configured regular expression
+
+This rule enforces the suite descriptions to follow the desired format. 
+
+## Rule Details
+
+By default, the regular expression is not configured and would be required if rule is enabled. 
+By default, the rule supports "describe", "context" and "suite" suite function names, but it can be configured to look for different suite names via rule configuration.
+
+Example of a custom rule configuration:
+
+```js
+rules: {
+   "mocha/valid-suite-description": ["warning", "^[A-Z]"]
+},
+```
+
+where:
+
+ * `warning` is a rule error level (see [Configuring Rules](http://eslint.org/docs/user-guide/configuring#configuring-rules))
+ * `^[A-Z]` is a custom regular expression pattern to match suite names against; `^[A-Z]` enforces a suite name to start with an upper-case letter
+
+The following patterns are considered warnings (with the example rule configuration posted above):
+
+```js
+// bdd
+describe("something to test", function() { });
+context("something to test", function() { });
+
+// tdd
+suite("something to test", function() { });
+```
+
+These patterns would not be considered warnings:
+
+```js
+// bdd
+describe("Test suite", function() { });
+context("Test suite", function() { });
+
+// tdd
+suite("Test suite", function() { });
+```
+
+There is also possible to configure a custom list of suite names via the second rule configuration option:
+
+```js
+rules: {
+   "mocha/valid-suite-description": ["warning", "^[A-Z]", ["describe", "context", "suite", "mysuitename"]]
+},
+```
diff --git a/index.js b/index.js
index 62ea6c8..3c4fc0b 100644
--- a/index.js
+++ b/index.js
@@ -8,7 +8,8 @@ module.exports = {
         'handle-done-callback': require('./lib/rules/handle-done-callback'),
         'no-synchronous-tests': require('./lib/rules/no-synchronous-tests'),
         'no-global-tests': require('./lib/rules/no-global-tests'),
-        'valid-test-description': require('./lib/rules/valid-test-description')
+        'valid-test-description': require('./lib/rules/valid-test-description'),
+        'valid-suite-description': require('./lib/rules/valid-suite-description')
     },
     configs: {
         recommended: {
diff --git a/lib/rules/valid-suite-description.js b/lib/rules/valid-suite-description.js
new file mode 100644
index 0000000..f7d2c94
--- /dev/null
+++ b/lib/rules/valid-suite-description.js
@@ -0,0 +1,27 @@
+'use strict';
+
+/**
+ * @fileoverview Match suite descriptions to match a pre-configured regular expression
+ * @author Alexander Afanasyev
+ */
+
+var defaultSuiteNames = [ 'describe', 'context', 'suite' ];
+
+module.exports = function (context) {
+    var pattern = new RegExp(context.options[0]),
+        suiteNames = context.options[1] ? context.options[1] : defaultSuiteNames;
+
+    return {
+        CallExpression: function (node) {
+            var callee = node.callee,
+                args;
+
+            if (callee && callee.name && suiteNames.indexOf(callee.name) > -1) {
+                args = node.arguments;
+                if (args && args[0] && typeof args[0].value === 'string' && !pattern.test(args[0].value)) {
+                    context.report(node, 'Invalid "' + callee.name + '()" description found.');
+                }
+            }
+        }
+    };
+};


Test patch:
diff --git a/test/rules/valid-suite-description.js b/test/rules/valid-suite-description.js
new file mode 100644
index 0000000..642d7b3
--- /dev/null
+++ b/test/rules/valid-suite-description.js
@@ -0,0 +1,62 @@
+'use strict';
+
+var RuleTester = require('eslint').RuleTester,
+    rules = require('../../').rules,
+    ruleTester = new RuleTester();
+
+ruleTester.run('valid-suite-description', rules['valid-suite-description'], {
+    valid: [
+        {
+            options: [ '^[A-Z]' ],
+            code: 'describe("This is a test", function () { });'
+        },
+        {
+            options: [ '^[A-Z]' ],
+            code: 'context("This is a test", function () { });'
+        },
+        {
+            options: [ '^[A-Z]' ],
+            code: 'suite("This is a test", function () { });'
+        },
+        {
+            options: [ '^[A-Z]', [ 'someFunction' ] ],
+            code: 'describe("this is a test", function () { });'
+        },
+        {
+            options: [ '^[A-Z]', [ 'someFunction' ] ],
+            code: 'someFunction("Should do something", function () { });'
+        },
+        'someOtherFunction();'
+    ],
+
+    invalid: [
+        {
+            options: [ '^[A-Z]' ],
+            code: 'describe("this is a test", function () { });',
+            errors: [
+                { message: 'Invalid "describe()" description found.' }
+            ]
+        },
+        {
+            options: [ '^[A-Z]' ],
+            code: 'context("this is a test", function () { });',
+            errors: [
+                { message: 'Invalid "context()" description found.' }
+            ]
+        },
+        {
+            options: [ '^[A-Z]' ],
+            code: 'suite("this is a test", function () { });',
+            errors: [
+                { message: 'Invalid "suite()" description found.' }
+            ]
+        },
+        {
+            options: [ '^[A-Z]', [ 'customFunction' ] ],
+            code: 'customFunction("this is a test", function () { });',
+            errors: [
+                { message: 'Invalid "customFunction()" description found.' }
+            ]
+        }
+    ]
+});