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

Issue:
Rule proposal: enforce it() description to start with "should"
Would it make sense to enforce having descriptive test names by warning if `it()` description does not start with "should". Example of violation:

```
it("I want to test something", function () {
  // ...
});
```

Valid usage:

```
it("should test something", function () {
  // ...
});
```

Thanks.

Pull request description:
feat(rules): add 'valid-test-description' rule
This should close #66. Will create a separate similar issue for describe/suite.

Please review and feel free to criticize/improve/discuss. Thanks!

Patch:
diff --git a/docs/rules/README.md b/docs/rules/README.md
index c501b8c..a9a60cb 100644
--- a/docs/rules/README.md
+++ b/docs/rules/README.md
@@ -6,3 +6,4 @@
 * [handle-done-callback](handle-done-callback.md) - enforces handling of callbacks for async tests
 * [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
diff --git a/index.js b/index.js
index 74ee99f..840b56b 100644
--- a/index.js
+++ b/index.js
@@ -7,6 +7,7 @@ module.exports = {
         'no-skipped-tests': require('./lib/rules/no-skipped-tests'),
         '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')
+        'no-global-tests': require('./lib/rules/no-global-tests'),
+        'valid-test-description': require('./lib/rules/valid-test-description')
     }
 };


Test patch:
diff --git a/docs/rules/valid-test-description.md b/docs/rules/valid-test-description.md
new file mode 100644
index 0000000..f2a3bc7
--- /dev/null
+++ b/docs/rules/valid-test-description.md
@@ -0,0 +1,47 @@
+# Match test descriptions against a pre-configured regular expression
+
+This rule enforces the test descriptions to follow the desired format. 
+
+## Rule Details
+
+By default, the regular expression is configured to be "^should" which requires test descriptions to start with "should". 
+By default, the rule supports "it", "specify" and "test" test function names, but it can be configured to look for different test names via rule configuration.
+
+Example of a custom rule configuration:
+
+```js
+   rules: {
+       "mocha/valid-test-description": ["warning", "mypattern$", ["it", "specify", "test", "mytestname"]]
+   },
+```
+
+where:
+
+ * `warning` is a rule error level (see [Configuring Rules](http://eslint.org/docs/user-guide/configuring#configuring-rules))
+ * `mypattern$` is a custom regular expression pattern to match test names against
+ * `["it", "specify", "test", "mytestname"]` is an array of custom test names 
+
+The following patterns are considered warnings (with the default rule configuration):
+
+```js
+// bdd
+it("does something", function() { });
+specify("does something", function() { });
+
+// tdd
+test("does something", function() { });
+```
+
+These patterns would not be considered warnings:
+
+```js
+// bdd
+it("should respond to GET", function() { });
+it("should do something");
+specify("should respond to GET", function() { });
+specify("should do something");
+
+// tdd
+test("should respond to GET", function() { });
+test("should do something");
+```
diff --git a/lib/rules/valid-test-description.js b/lib/rules/valid-test-description.js
new file mode 100644
index 0000000..1021bdc
--- /dev/null
+++ b/lib/rules/valid-test-description.js
@@ -0,0 +1,27 @@
+'use strict';
+
+/**
+ * @fileoverview Match test descriptions to match a pre-configured regular expression
+ * @author Alexander Afanasyev
+ */
+
+var defaultTestNames = [ 'it', 'test', 'specify' ];
+
+module.exports = function (context) {
+    var pattern = context.options[0] ? new RegExp(context.options[0]) : /^should/,
+        testNames = context.options[1] ? context.options[1] : defaultTestNames;
+
+    return {
+        CallExpression: function (node) {
+            var callee = node.callee,
+                args;
+
+            if (callee && callee.name && testNames.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.');
+                }
+            }
+        }
+    };
+};
diff --git a/test/rules/valid-test-description.js b/test/rules/valid-test-description.js
new file mode 100644
index 0000000..601d23e
--- /dev/null
+++ b/test/rules/valid-test-description.js
@@ -0,0 +1,85 @@
+'use strict';
+
+var RuleTester = require('eslint').RuleTester,
+    rules = require('../../').rules,
+    ruleTester = new RuleTester();
+
+ruleTester.run('valid-test-description', rules['valid-test-description'], {
+    valid: [
+        'it("should respond to GET", function() { });',
+        'it("should do something");',
+        'specify("should respond to GET", function() { });',
+        'specify("should do something");',
+        'test("should respond to GET", function() { });',
+        'test("should do something");',
+        'it();',
+        'specify();',
+        'test();',
+        {
+            options: [ 'test' ],
+            code: 'it("this is a test", function () { });'
+        },
+        {
+            options: [ 'test' ],
+            code: 'test("this is a test", function () { });'
+        },
+        {
+            options: [ '^should', [ 'someFunction' ] ],
+            code: 'it("this is a test", function () { });'
+        },
+        {
+            options: [ '^should', [ 'someFunction' ] ],
+            code: 'someFunction("should do something", function () { });'
+        },
+        'someOtherFunction();'
+    ],
+
+    invalid: [
+        {
+            code: 'it("does something", function() { });',
+            errors: [
+                { message: 'Invalid "it()" description found.' }
+            ]
+        },
+        {
+            code: 'specify("does something", function() { });',
+            errors: [
+                { message: 'Invalid "specify()" description found.' }
+            ]
+        },
+        {
+            code: 'test("does something", function() { });',
+            errors: [
+                { message: 'Invalid "test()" description found.' }
+            ]
+        },
+        {
+            options: [ 'required' ],
+            code: 'it("this is a test", function () { });',
+            errors: [
+                { message: 'Invalid "it()" description found.' }
+            ]
+        },
+        {
+            options: [ 'required' ],
+            code: 'specify("this is a test", function () { });',
+            errors: [
+                { message: 'Invalid "specify()" description found.' }
+            ]
+        },
+        {
+            options: [ 'required' ],
+            code: 'test("this is a test", function () { });',
+            errors: [
+                { message: 'Invalid "test()" description found.' }
+            ]
+        },
+        {
+            options: [ 'required', [ 'customFunction' ] ],
+            code: 'customFunction("this is a test", function () { });',
+            errors: [
+                { message: 'Invalid "customFunction()" description found.' }
+            ]
+        }
+    ]
+});