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

Issue:
Rule proposal: avoid before / after hooks
Using `before` or `after` hooks is almost always a sign that you use shared state between multiple tests. This could lead to unwanted side-effects, e.g. some tests only pass/fail if the tests are executed in a specific order.

Pull request:
Add rule `no-hooks` (fixes #39)
Add rule `no-hooks` (fixes #39)

Let me know if you want me to change something or if I've missed something :)

Patch:
diff --git a/docs/rules/README.md b/docs/rules/README.md
index a39c64c..df8f4cd 100644
--- a/docs/rules/README.md
+++ b/docs/rules/README.md
@@ -9,3 +9,4 @@
 * [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
 * [no-mocha-arrows](no-mocha-arrows.md) - disallow arrow functions as arguments to mocha globals
+* [no-hooks](no-hooks.md) - disallow hooks
diff --git a/docs/rules/no-hooks.md b/docs/rules/no-hooks.md
new file mode 100644
index 0000000..276673d
--- /dev/null
+++ b/docs/rules/no-hooks.md
@@ -0,0 +1,48 @@
+# Disallow hooks (no-hooks)
+
+Mocha proposes hooks that allow code to be run before or after every or all tests. This helps define a common setup or teardown process for every test. The use of these hooks promotes the use of shared state between the tests, and defeats the purpose of having isolated unit tests.
+
+## Rule Details
+
+This rule looks for every call to `before`, `after`, `beforeEach` and `afterEach`.
+
+The following patterns are considered warnings:
+
+```js
+describe('foo', function () {
+    var mockUser;
+
+    before(function () { // Not allowed
+        mockUser = {age: 50};
+    });
+
+    after(function () { /* ... */ }); // Not allowed
+    beforeEach(function () { /* ... */ }); // Not allowed
+    afterEach(function () { /* ... */ }); // Not allowed
+
+    it(function () {
+        assert.equals(lib.method(mockUser), 'expectedValue');
+    });
+});
+```
+
+These patterns would not be considered warnings:
+
+```js
+function createFixtures() {
+    return {
+        mockUser: {age: 50}
+    };
+}
+
+describe('foo', function () {
+    it(function () {
+        var fixtures = createFixtures();
+        assert.equals(lib.method(fixtures.mockUser), 'expectedValue');
+    });
+});
+```
+
+## When Not To Use It
+
+* If you use another library which exposes a similar API as mocha (e.g. `before`, `after`), you should turn this rule off, because it would raise warnings.
diff --git a/index.js b/index.js
index 5dc2c0b..e018c8a 100644
--- a/index.js
+++ b/index.js
@@ -10,7 +10,8 @@ module.exports = {
         'no-global-tests': require('./lib/rules/no-global-tests'),
         'valid-test-description': require('./lib/rules/valid-test-description'),
         'valid-suite-description': require('./lib/rules/valid-suite-description'),
-        'no-mocha-arrows': require('./lib/rules/no-mocha-arrows')
+        'no-mocha-arrows': require('./lib/rules/no-mocha-arrows'),
+        'no-hooks': require('./lib/rules/no-hooks')
     },
     configs: {
         recommended: {
diff --git a/lib/rules/no-hooks.js b/lib/rules/no-hooks.js
new file mode 100644
index 0000000..6696de2
--- /dev/null
+++ b/lib/rules/no-hooks.js
@@ -0,0 +1,16 @@
+'use strict';
+
+module.exports = function (context) {
+    var hooks = [ 'before', 'after', 'beforeEach', 'afterEach' ];
+
+    return {
+        CallExpression: function (node) {
+            if (node.callee.type === 'Identifier' && hooks.indexOf(node.callee.name) !== -1) {
+                context.report({
+                    node: node.callee,
+                    message: 'Unexpected use of Mocha `' + node.callee.name + '` hook'
+                });
+            }
+        }
+    };
+};


Test patch:
diff --git a/test/rules/no-hooks.js b/test/rules/no-hooks.js
new file mode 100644
index 0000000..b013696
--- /dev/null
+++ b/test/rules/no-hooks.js
@@ -0,0 +1,47 @@
+'use strict';
+
+var RuleTester = require('eslint').RuleTester,
+    rules = require('../../').rules,
+    ruleTester = new RuleTester();
+
+ruleTester.run('no-hooks', rules['no-hooks'], {
+
+    valid: [
+        'describe(function() { it(function() {}); });',
+        'describe(function() { it(function() {}); it(function() {}); });',
+        'describe(function() { describe(function() { it(function() {}); }); });',
+        'foo.before()',
+        'foo.after()',
+        'foo.beforeEach()',
+        'foo.afterEach()',
+        'before.foo()',
+        'after.foo()',
+        'beforeEach.foo()',
+        'afterEach.foo()',
+        'var before = 2; before + 3;'
+    ],
+
+    invalid: [
+        {
+            code: 'describe(function() { before(function() {}); });',
+            errors: [ { message: 'Unexpected use of Mocha `before` hook', column: 23, line: 1 } ]
+        },
+        {
+            code: 'describe(function() { after(function() {}); });',
+            errors: [ { message: 'Unexpected use of Mocha `after` hook', column: 23, line: 1 } ]
+        },
+        {
+            code: 'describe(function() { beforeEach(function() {}); });',
+            errors: [ { message: 'Unexpected use of Mocha `beforeEach` hook', column: 23, line: 1 } ]
+        },
+        {
+            code: 'describe(function() { afterEach(function() {}); });',
+            errors: [ { message: 'Unexpected use of Mocha `afterEach` hook', column: 23, line: 1 } ]
+        },
+        {
+            code: 'describe(function() { describe(function() { before(function() {}); }); });',
+            errors: [ { message: 'Unexpected use of Mocha `before` hook', column: 45, line: 1 } ]
+        }
+    ]
+
+});