Repository: lo1tuma/eslint-plugin-mocha
Pull request #262 (2020-07-31 18:32:41)

Consistently use shared settings in all rules
**This is a breaking change.**

This changes the structure of the shared settings, in order to make it consistent to be used for all rules.


Patch:
diff --git a/README.md b/README.md
index 9f2d919..0f6f586 100644
--- a/README.md
+++ b/README.md
@@ -20,9 +20,31 @@ Then add a reference to this plugin and selected rules in your eslint config:
 
 ```json
 {
-  "plugins": [
-    "mocha"
-  ],
+    "plugins": [
+        "mocha"
+    ]
+}
+```
+
+### Plugin Settings
+
+This plugin supports the following settings, which are used by multiple rules:
+
+* `additionalCustomNames`: This allows rules to check additional function names when looking for suites or test cases. This might be used with a custom Mocha extension, such as [`ember-mocha`](https://github.com/switchfly/ember-mocha)
+**Example:**
+
+```json
+{
+    "rules": {
+        "mocha/no-skipped-tests": "error",
+        "mocha/no-exclusive-tests": "error"
+    },
+    "settings": {
+        "mocha/additionalCustomNames": [
+            { "name": "describeModule", "type": "suite", "interfaces": [ "BDD" ] },
+            { "name": "testModule", "type": "testCase", "interfaces": [ "TDD" ] }
+        }
+    }
 }
 ```
 
@@ -34,9 +56,9 @@ Enable it with the extends option:
 
 ```json
 {
-  "extends": [
-    "plugin:mocha/recommended"
-  ],
+    "extends": [
+        "plugin:mocha/recommended"
+    ]
 }
 ```
 
diff --git a/lib/rules/handle-done-callback.js b/lib/rules/handle-done-callback.js
index 49b6d3e..f5e5ea1 100644
--- a/lib/rules/handle-done-callback.js
+++ b/lib/rules/handle-done-callback.js
@@ -1,7 +1,7 @@
 'use strict';
 
 const find = require('ramda/src/find');
-const astUtils = require('../util/ast');
+const createAstUtils = require('../util/ast');
 
 module.exports = {
     meta: {
@@ -23,8 +23,8 @@ module.exports = {
         ]
     },
     create(context) {
-        const [ config = {} ] = context.options;
-        const { ignoreSkipped = false } = config;
+        const astUtils = createAstUtils(context.settings);
+        const [ { ignoreSkipped = false } = {} ] = context.options;
         const modifiersToCheck = ignoreSkipped ? [ 'only' ] : [ 'only', 'skip' ];
 
         function isAsyncFunction(functionExpression) {
diff --git a/lib/rules/max-top-level-suites.js b/lib/rules/max-top-level-suites.js
index 88b1848..80511a7 100644
--- a/lib/rules/max-top-level-suites.js
+++ b/lib/rules/max-top-level-suites.js
@@ -6,8 +6,7 @@
  */
 
 const isNil = require('ramda/src/isNil');
-const astUtil = require('../util/ast');
-const { additionalSuiteNames } = require('../util/settings');
+const createAstUtils = require('../util/ast');
 
 const defaultSuiteLimit = 1;
 
@@ -34,9 +33,9 @@ module.exports = {
         ]
     },
     create(context) {
+        const astUtils = createAstUtils(context.settings);
         const topLevelDescribes = [];
         const options = context.options[0] || {};
-        const settings = context.settings;
         let suiteLimit;
 
         if (isNil(options.limit)) {
@@ -47,7 +46,7 @@ module.exports = {
 
         return {
             CallExpression(node) {
-                if (astUtil.isDescribe(node, additionalSuiteNames(settings))) {
+                if (astUtils.isDescribe(node)) {
                     const scope = context.getScope();
 
                     if (isTopLevelScope(scope)) {
diff --git a/lib/rules/no-async-describe.js b/lib/rules/no-async-describe.js
index 9f9b9d5..e52bd1d 100644
--- a/lib/rules/no-async-describe.js
+++ b/lib/rules/no-async-describe.js
@@ -6,8 +6,7 @@
  * @fileoverview Disallow async functions as arguments to describe
  */
 
-const astUtils = require('../util/ast');
-const { additionalSuiteNames } = require('../util/settings');
+const createAstUtils = require('../util/ast');
 
 module.exports = {
     meta: {
@@ -18,6 +17,7 @@ module.exports = {
         fixable: 'code'
     },
     create(context) {
+        const astUtils = createAstUtils(context.settings);
         const sourceCode = context.getSourceCode();
 
         function isFunction(node) {
@@ -62,7 +62,7 @@ module.exports = {
             CallExpression(node) {
                 const name = astUtils.getNodeName(node.callee);
 
-                if (astUtils.isDescribe(node, additionalSuiteNames(context.settings))) {
+                if (astUtils.isDescribe(node)) {
                     const fnArg = node.arguments.slice(-1)[0];
                     if (isAsyncFunction(fnArg)) {
                         context.report({
diff --git a/lib/rules/no-hooks-for-single-case.js b/lib/rules/no-hooks-for-single-case.js
index 2645814..0dd6e12 100644
--- a/lib/rules/no-hooks-for-single-case.js
+++ b/lib/rules/no-hooks-for-single-case.js
@@ -1,7 +1,6 @@
 'use strict';
 
-const astUtil = require('../util/ast');
-const { additionalSuiteNames } = require('../util/settings');
+const createAstUtils = require('../util/ast');
 
 function newDescribeLayer(describeNode) {
     return {
@@ -32,9 +31,9 @@ module.exports = {
         ]
     },
     create(context) {
+        const astUtils = createAstUtils(context.settings);
         const options = context.options[0] || {};
         const allowedHooks = options.allow || [];
-        const settings = context.settings;
         let layers = [];
 
         function increaseTestCount() {
@@ -72,17 +71,17 @@ module.exports = {
             },
 
             CallExpression(node) {
-                if (astUtil.isDescribe(node, additionalSuiteNames(settings))) {
+                if (astUtils.isDescribe(node)) {
                     increaseTestCount();
                     layers.push(newDescribeLayer(node));
                     return;
                 }
 
-                if (astUtil.isTestCase(node)) {
+                if (astUtils.isTestCase(node)) {
                     increaseTestCount();
                 }
 
-                if (astUtil.isHookIdentifier(node.callee)) {
+                if (astUtils.isHookIdentifier(node.callee)) {
                     layers[layers.length - 1].hookNodes.push(node.callee);
                 }
             },
diff --git a/lib/rules/no-hooks.js b/lib/rules/no-hooks.js
index 02eff9c..86fdf63 100644
--- a/lib/rules/no-hooks.js
+++ b/lib/rules/no-hooks.js
@@ -1,6 +1,6 @@
 'use strict';
 
-const astUtil = require('../util/ast');
+const createAstUtils = require('../util/ast');
 
 module.exports = {
     meta: {
@@ -25,6 +25,7 @@ module.exports = {
     },
 
     create(context) {
+        const astUtils = createAstUtils(context.settings);
         const [ config = {} ] = context.options;
         const { allow = [] } = config;
 
@@ -32,7 +33,7 @@ module.exports = {
             CallExpression(node) {
                 const isHookAllowed = allow.includes(node.callee.name);
 
-                if (astUtil.isHookIdentifier(node.callee) && !isHookAllowed) {
+                if (astUtils.isHookIdentifier(node.callee) && !isHookAllowed) {
                     context.report({
                         node: node.callee,
                         message: `Unexpected use of Mocha \`${ node.callee.name }\` hook`
diff --git a/lib/rules/no-identical-title.js b/lib/rules/no-identical-title.js
index 82e98dd..678dbd9 100644
--- a/lib/rules/no-identical-title.js
+++ b/lib/rules/no-identical-title.js
@@ -1,7 +1,6 @@
 'use strict';
 
-const astUtil = require('../util/ast');
-const { additionalSuiteNames } = require('../util/settings');
+const createAstUtils = require('../util/ast');
 
 function newLayer() {
     return {
@@ -10,33 +9,6 @@ function newLayer() {
     };
 }
 
-function handlTestCaseTitles(context, titles, node, title) {
-    if (astUtil.isTestCase(node)) {
-        if (titles.includes(title)) {
-            context.report({
-                node,
-                message: 'Test title is used multiple times in the same test suite.'
-            });
-        }
-        titles.push(title);
-    }
-}
-
-function handlTestSuiteTitles(context, titles, node, title) {
-    const settings = context.settings;
-
-    if (!astUtil.isDescribe(node, additionalSuiteNames(settings))) {
-        return;
-    }
-    if (titles.includes(title)) {
-        context.report({
-            node,
-            message: 'Test suite title is used multiple times.'
-        });
-    }
-    titles.push(title);
-}
-
 function isFirstArgLiteral(node) {
     return node.arguments && node.arguments[0] && node.arguments[0].type === 'Literal';
 }
@@ -49,14 +21,39 @@ module.exports = {
         }
     },
     create(context) {
+        const astUtils = createAstUtils(context.settings);
         const titleLayers = [ newLayer() ];
-        const settings = context.settings;
+
+        function handlTestCaseTitles(titles, node, title) {
+            if (astUtils.isTestCase(node)) {
+                if (titles.includes(title)) {
+                    context.report({
+                        node,
+                        message: 'Test title is used multiple times in the same test suite.'
+                    });
+                }
+                titles.push(title);
+            }
+        }
+
+        function handlTestSuiteTitles(titles, node, title) {
+            if (!astUtils.isDescribe(node)) {
+                return;
+            }
+            if (titles.includes(title)) {
+                context.report({
+                    node,
+                    message: 'Test suite title is used multiple times.'
+                });
+            }
+            titles.push(title);
+        }
 
         return {
             CallExpression(node) {
                 const currentLayer = titleLayers[titleLayers.length - 1];
 
-                if (astUtil.isDescribe(node, additionalSuiteNames(settings))) {
+                if (astUtils.isDescribe(node)) {
                     titleLayers.push(newLayer());
                 }
                 if (!isFirstArgLiteral(node)) {
@@ -64,11 +61,11 @@ module.exports = {
                 }
 
                 const title = node.arguments[0].value;
-                handlTestCaseTitles(context, currentLayer.testTitles, node, title);
-                handlTestSuiteTitles(context, currentLayer.describeTitles, node, title);
+                handlTestCaseTitles(currentLayer.testTitles, node, title);
+                handlTestSuiteTitles(currentLayer.describeTitles, node, title);
             },
             'CallExpression:exit'(node) {
-                if (astUtil.isDescribe(node, additionalSuiteNames(settings))) {
+                if (astUtils.isDescribe(node)) {
                     titleLayers.pop();
                 }
             }
diff --git a/lib/rules/no-mocha-arrows.js b/lib/rules/no-mocha-arrows.js
index e6559fc..aa84eaf 100644
--- a/lib/rules/no-mocha-arrows.js
+++ b/lib/rules/no-mocha-arrows.js
@@ -5,7 +5,7 @@
  * @author Paul Melnikow
  */
 
-const astUtils = require('../util/ast');
+const createAstUtils = require('../util/ast');
 
 module.exports = {
     meta: {
@@ -16,6 +16,7 @@ module.exports = {
         fixable: 'code'
     },
     create(context) {
+        const astUtils = createAstUtils(context.settings);
         const sourceCode = context.getSourceCode();
 
         function extractSourceTextByRange(start, end) {
diff --git a/lib/rules/no-return-and-callback.js b/lib/rules/no-return-and-callback.js
index 453d7cf..54b0ad4 100644
--- a/lib/rules/no-return-and-callback.js
+++ b/lib/rules/no-return-and-callback.js
@@ -1,6 +1,6 @@
 'use strict';
 
-const astUtils = require('../util/ast');
+const createAstUtils = require('../util/ast');
 
 function reportIfShortArrowFunction(context, node) {
     if (node.body.type !== 'BlockStatement') {
@@ -19,26 +19,6 @@ function isFunctionCallWithName(node, name) {
         node.callee.name === name;
 }
 
-function isAllowedReturnStatement(node, doneName) {
-    const argument = node.argument;
-
-    if (astUtils.isReturnOfUndefined(node) || argument.type === 'Literal') {
-        return true;
-    }
-
-    return isFunctionCallWithName(argument, doneName);
-}
-
-function reportIfFunctionWithBlock(context, node, doneName) {
-    const returnStatement = astUtils.findReturnStatement(node.body.body);
-    if (returnStatement && !isAllowedReturnStatement(returnStatement, doneName)) {
-        context.report({
-            node: returnStatement,
-            message: 'Unexpected use of `return` in a test with callback'
-        });
-    }
-}
-
 module.exports = {
     meta: {
         type: 'problem',
@@ -47,13 +27,35 @@ module.exports = {
         }
     },
     create(context) {
+        const astUtils = createAstUtils(context.settings);
+
+        function isAllowedReturnStatement(node, doneName) {
+            const argument = node.argument;
+
+            if (astUtils.isReturnOfUndefined(node) || argument.type === 'Literal') {
+                return true;
+            }
+
+            return isFunctionCallWithName(argument, doneName);
+        }
+
+        function reportIfFunctionWithBlock(node, doneName) {
+            const returnStatement = astUtils.findReturnStatement(node.body.body);
+            if (returnStatement && !isAllowedReturnStatement(returnStatement, doneName)) {
+                context.report({
+                    node: returnStatement,
+                    message: 'Unexpected use of `return` in a test with callback'
+                });
+            }
+        }
+
         function check(node) {
             if (node.params.length === 0 || !astUtils.hasParentMochaFunctionCall(node)) {
                 return;
             }
 
             if (!reportIfShortArrowFunction(context, node)) {
-                reportIfFunctionWithBlock(context, node, node.params[0].name);
+                reportIfFunctionWithBlock(node, node.params[0].name);
             }
         }
 
diff --git a/lib/rules/no-return-from-async.js b/lib/rules/no-return-from-async.js
index 3185a61..590c99e 100644
--- a/lib/rules/no-return-from-async.js
+++ b/lib/rules/no-return-from-async.js
@@ -1,6 +1,6 @@
 'use strict';
 
-const astUtils = require('../util/ast');
+const createAstUtils = require('../util/ast');
 
 function reportIfShortArrowFunction(context, node) {
     if (node.body.type !== 'BlockStatement') {
@@ -13,26 +13,6 @@ function reportIfShortArrowFunction(context, node) {
     return false;
 }
 
-function isAllowedReturnStatement(node) {
-    const argument = node.argument;
-
-    if (astUtils.isReturnOfUndefined(node) || argument.type === 'Literal') {
-        return true;
-    }
-
-    return false;
-}
-
-function reportIfFunctionWithBlock(context, node) {
-    const returnStatement = astUtils.findReturnStatement(node.body.body);
-    if (returnStatement && !isAllowedReturnStatement(returnStatement)) {
-        context.report({
-            node: returnStatement,
-            message: 'Unexpected use of `return` in a test with an async function'
-        });
-    }
-}
-
 module.exports = {
     meta: {
         type: 'suggestion',
@@ -41,13 +21,36 @@ module.exports = {
         }
     },
     create(context) {
+        const astUtils = createAstUtils(context.settings);
+
+        function isAllowedReturnStatement(node) {
+            const argument = node.argument;
+
+            if (astUtils.isReturnOfUndefined(node) || argument.type === 'Literal') {
+                return true;
+            }
+
+            return false;
+        }
+
+        function reportIfFunctionWithBlock(node) {
+            const returnStatement = astUtils.findReturnStatement(node.body.body);
+
+            if (returnStatement && !isAllowedReturnStatement(returnStatement)) {
+                context.report({
+                    node: returnStatement,
+                    message: 'Unexpected use of `return` in a test with an async function'
+                });
+            }
+        }
+
         function check(node) {
             if (!node.async || !astUtils.hasParentMochaFunctionCall(node)) {
                 return;
             }
 
             if (!reportIfShortArrowFunction(context, node)) {
-                reportIfFunctionWithBlock(context, node);
+                reportIfFunctionWithBlock(node);
             }
         }
 
diff --git a/lib/rules/no-setup-in-describe.js b/lib/rules/no-setup-in-describe.js
index 90cd322..d34714a 100644
--- a/lib/rules/no-setup-in-describe.js
+++ b/lib/rules/no-setup-in-describe.js
@@ -1,7 +1,6 @@
 'use strict';
 
-const astUtils = require('../util/ast');
-const { additionalSuiteNames } = require('../util/settings');
+const createAstUtils = require('../util/ast');
 
 const FUNCTION = 1;
 const DESCRIBE = 2;
@@ -17,7 +16,7 @@ module.exports = {
     },
     create(context) {
         const nesting = [];
-        const settings = context.settings;
+        const astUtils = createAstUtils(context.settings);
 
         function isPureNode(node) {
             return astUtils.isHookCall(node) ||
@@ -59,7 +58,7 @@ module.exports = {
         }
 
         function isDescribe(node) {
-            return astUtils.isDescribe(node, additionalSuiteNames(settings));
+            return astUtils.isDescribe(node);
         }
 
         function isParentDescribe(node) {
diff --git a/lib/rules/no-sibling-hooks.js b/lib/rules/no-sibling-hooks.js
index 08f433f..deae1ff 100644
--- a/lib/rules/no-sibling-hooks.js
+++ b/lib/rules/no-sibling-hooks.js
@@ -1,7 +1,6 @@
 'use strict';
 
-const astUtil = require('../util/ast');
-const { additionalSuiteNames } = require('../util/settings');
+const createAstUtils = require('../util/ast');
 
 function newDescribeLayer(describeNode) {
     return {
@@ -22,7 +21,7 @@ module.exports = {
     },
     create(context) {
         const isUsed = [];
-        const settings = context.settings;
+        const astUtils = createAstUtils(context.settings);
 
         return {
             Program(node) {
@@ -30,14 +29,14 @@ module.exports = {
             },
 
             CallExpression(node) {
-                const name = astUtil.getNodeName(node.callee);
+                const name = astUtils.getNodeName(node.callee);
 
-                if (astUtil.isDescribe(node, additionalSuiteNames(settings))) {
+                if (astUtils.isDescribe(node)) {
                     isUsed.push(newDescribeLayer(node));
                     return;
                 }
 
-                if (!astUtil.isHookIdentifier(node.callee)) {
+                if (!astUtils.isHookIdentifier(node.callee)) {
                     return;
                 }
 
diff --git a/lib/rules/no-top-level-hooks.js b/lib/rules/no-top-level-hooks.js
index 3128b4e..5ae09a0 100644
--- a/lib/rules/no-top-level-hooks.js
+++ b/lib/rules/no-top-level-hooks.js
@@ -1,7 +1,6 @@
 'use strict';
 
-const astUtil = require('../util/ast');
-const { additionalSuiteNames } = require('../util/settings');
+const createAstUtils = require('../util/ast');
 
 module.exports = {
     meta: {
@@ -11,17 +10,17 @@ module.exports = {
         }
     },
     create(context) {
-        const settings = context.settings;
+        const astUtils = createAstUtils(context.settings);
         const testSuiteStack = [];
 
         return {
             CallExpression(node) {
-                if (astUtil.isDescribe(node, additionalSuiteNames(settings))) {
+                if (astUtils.isDescribe(node)) {
                     testSuiteStack.push(node);
                     return;
                 }
 
-                if (!astUtil.isHookIdentifier(node.callee)) {
+                if (!astUtils.isHookIdentifier(node.callee)) {
                     return;
                 }
 
diff --git a/lib/rules/prefer-arrow-callback.js b/lib/rules/prefer-arrow-callback.js
index 1a341b8..2493018 100644
--- a/lib/rules/prefer-arrow-callback.js
+++ b/lib/rules/prefer-arrow-callback.js
@@ -12,7 +12,7 @@
  * @author Toru Nagashima (core eslint rule)
  * @author Michael Fields (mocha-aware rule modifications)
  */
-const astUtils = require('../util/ast');
+const createAstUtils = require('../util/ast');
 
 // ------------------------------------------------------------------------------
 // Helpers
@@ -75,66 +75,6 @@ function isBindThis(node, currentNode) {
         node.parent.callee === node;
 }
 
-/**
- * Checkes whether or not a given node is a callback.
- * @param {ASTNode} node - A node to check.
- * @param {object} context - The eslint context.
- * @returns {Object}
- *   {boolean} retv.isCallback - `true` if the node is a callback.
- *   {boolean} retv.isMochaCallback - `true` if the node is an argument to a mocha function.
- *   {boolean} retv.isLexicalThis - `true` if the node is with `.bind(this)`.
- */
-function getCallbackInfo(node, context) {
-    const retv = { isCallback: false, isLexicalThis: false };
-    let searchComplete = false;
-    let currentNode = node;
-    let parent = node.parent;
-
-    while (currentNode && !searchComplete) {
-        switch (parent.type) {
-        // Checks parents recursively.
-
-        case 'LogicalExpression':
-        case 'ConditionalExpression':
-            break;
-
-            // Checks whether the parent node is `.bind(this)` call.
-        case 'MemberExpression':
-            if (isBindThis(parent, currentNode)) {
-                retv.isLexicalThis =
-                        parent.parent.arguments.length === 1 &&
-                        parent.parent.arguments[0].type === 'ThisExpression';
-                parent = parent.parent;
-            } else {
-                searchComplete = true;
-            }
-            break;
-
-            // Checks whether the node is a callback.
-        case 'CallExpression':
-        case 'NewExpression':
-            if (parent.callee !== currentNode) {
-                retv.isCallback = true;
-            }
-            // Checks whether the node is a mocha function callback.
-            if (retv.isCallback && astUtils.isMochaFunctionCall(parent, context.getScope())) {
-                retv.isMochaCallback = true;
-            }
-            searchComplete = true;
-            break;
-
-        default:
-            searchComplete = true;
-        }
-
-        if (!searchComplete) {
-            currentNode = parent;
-            parent = parent.parent;
-        }
-    }
-    return retv;
-}
-
 /**
  * Checks whether a simple list of parameters contains any duplicates. This does not handle complex
  * parameter lists (e.g. with destructuring), since complex parameter lists are a SyntaxError with duplicate
@@ -181,6 +121,7 @@ module.exports = {
     },
 
     create(context) {
+        const astUtils = createAstUtils(context.settings);
         const options = context.options[0] || {};
 
         // allowUnboundThis defaults to true
@@ -188,6 +129,66 @@ module.exports = {
         const allowNamedFunctions = options.allowNamedFunctions;
         const sourceCode = context.getSourceCode();
 
+        /**
+         * Checkes whether or not a given node is a callback.
+         * @param {ASTNode} node - A node to check.
+         * @param {object} context - The eslint context.
+         * @returns {Object}
+         *   {boolean} retv.isCallback - `true` if the node is a callback.
+         *   {boolean} retv.isMochaCallback - `true` if the node is an argument to a mocha function.
+         *   {boolean} retv.isLexicalThis - `true` if the node is with `.bind(this)`.
+         */
+        function getCallbackInfo(node) {
+            const retv = { isCallback: false, isLexicalThis: false };
+            let searchComplete = false;
+            let currentNode = node;
+            let parent = node.parent;
+
+            while (currentNode && !searchComplete) {
+                switch (parent.type) {
+                // Checks parents recursively.
+
+                case 'LogicalExpression':
+                case 'ConditionalExpression':
+                    break;
+
+                    // Checks whether the parent node is `.bind(this)` call.
+                case 'MemberExpression':
+                    if (isBindThis(parent, currentNode)) {
+                        retv.isLexicalThis =
+                        parent.parent.arguments.length === 1 &&
+                        parent.parent.arguments[0].type === 'ThisExpression';
+                        parent = parent.parent;
+                    } else {
+                        searchComplete = true;
+                    }
+                    break;
+
+                    // Checks whether the node is a callback.
+                case 'CallExpression':
+                case 'NewExpression':
+                    if (parent.callee !== currentNode) {
+                        retv.isCallback = true;
+                    }
+                    // Checks whether the node is a mocha function callback.
+                    if (retv.isCallback && astUtils.isMochaFunctionCall(parent, context.getScope())) {
+                        retv.isMochaCallback = true;
+                    }
+                    searchComplete = true;
+                    break;
+
+                default:
+                    searchComplete = true;
+                }
+
+                if (!searchComplete) {
+                    currentNode = parent;
+                    parent = parent.parent;
+                }
+            }
+            return retv;
+        }
+
         /*
          * {Array<{this: boolean, meta: boolean}>}
          * - this - A flag which shows there are one or more ThisExpression.
diff --git a/lib/util/ast.js b/lib/util/ast.js
index 4fa15f2..d2e2bba 100644
--- a/lib/util/ast.js
+++ b/lib/util/ast.js
@@ -7,6 +7,7 @@ const propEq = require('ramda/src/propEq');
 const pathEq = require('ramda/src/pathEq');
 const find = require('ramda/src/find');
 const { getTestCaseNames, getSuiteNames } = require('./names');
+const { getAddtionalNames } = require('./settings');
 
 const isDefined = complement(isNil);
 const isCallExpression = both(isDefined, propEq('type', 'CallExpression'));
@@ -17,6 +18,8 @@ const hooks = [
 ];
 const suiteConfig = [ 'timeout', 'slow', 'retries' ];
 
+const findReturnStatement = find(propEq('type', 'ReturnStatement'));
+
 function getPropertyName(property) {
     return property.name || property.value;
 }
@@ -28,12 +31,6 @@ function getNodeName(node) {
     return node.name;
 }
 
-function isDescribe(node, additionalSuiteNames = []) {
-    const describeAliases = getSuiteNames({ modifiers: [ 'skip', 'only' ], additionalSuiteNames });
-
-    return isCallExpression(node) && describeAliases.includes(getNodeName(node.callee));
-}
-
 function isHookIdentifier(node) {
     return node &&
       node.type === 'Identifier' &&
@@ -44,31 +41,6 @@ function isHookCall(node) {
     return isCallExpression(node) && isHookIdentifier(node.callee);
 }
 
-function isTestCase(node, options = {}) {
-    const { modifiers = [ 'skip', 'only' ] } = options;
-
-    const testCaseNames = getTestCaseNames({ modifiers });
-    return isCallExpression(node) && testCaseNames.includes(getNodeName(node.callee));
-}
-
-function isSuiteConfigExpression(node) {
-    if (node.type !== 'MemberExpression') {
-        return false;
-    }
-
-    const usingThis = node.object.type === 'ThisExpression';
-
-    if (usingThis || isTestCase(node.object)) {
-        return suiteConfig.includes(getPropertyName(node.property));
-    }
-
-    return false;
-}
-
-function isSuiteConfigCall(node) {
-    return isCallExpression(node) && isSuiteConfigExpression(node.callee);
-}
-
 function findReference(scope, node) {
     const hasSameRangeAsNode = pathEq([ 'identifier', 'range' ], node.range);
 
@@ -87,41 +59,81 @@ function isCallToShadowedReference(node, scope) {
     return isShadowed(scope, identifier);
 }
 
-function isMochaFunctionCall(node, scope) {
-    if (isCallToShadowedReference(node, scope)) {
+function isFunctionCallWithName(node, names) {
+    return isCallExpression(node) && names.includes(getNodeName(node.callee));
+}
+
+function createAstUtils(settings) {
+    const additionalCustomNames = getAddtionalNames(settings);
+
+    function isDescribe(node, options = {}) {
+        const { modifiers = [ 'skip', 'only' ], modifiersOnly = false } = options;
+        const describeAliases = getSuiteNames({ modifiersOnly, modifiers, additionalCustomNames });
+
+        return isFunctionCallWithName(node, describeAliases);
+    }
+
+    function isTestCase(node, options = {}) {
+        const { modifiers = [ 'skip', 'only' ], modifiersOnly = false } = options;
+        const testCaseNames = getTestCaseNames({ modifiersOnly, modifiers, additionalCustomNames });
+
+        return isFunctionCallWithName(node, testCaseNames);
+    }
+
+    function isSuiteConfigExpression(node) {
+        if (node.type !== 'MemberExpression') {
+            return false;
+        }
+
+        const usingThis = node.object.type === 'ThisExpression';
+
+        if (usingThis || isTestCase(node.object)) {
+            return suiteConfig.includes(getPropertyName(node.property));
+        }
+
         return false;
     }
 
-    return isTestCase(node) || isDescribe(node) || isHookCall(node);
-}
+    function isSuiteConfigCall(node) {
+        return isCallExpression(node) && isSuiteConfigExpression(node.callee);
+    }
 
-function hasParentMochaFunctionCall(functionExpression, options) {
-    return isTestCase(functionExpression.parent, options) || isHookCall(functionExpression.parent);
-}
+    function isMochaFunctionCall(node, scope) {
+        if (isCallToShadowedReference(node, scope)) {
+            return false;
+        }
 
-function isExplicitUndefined(node) {
-    return node && node.type === 'Identifier' && node.name === 'undefined';
-}
+        return isTestCase(node) || isDescribe(node) || isHookCall(node);
+    }
 
-function isReturnOfUndefined(node) {
-    const argument = node.argument;
-    const isImplicitUndefined = argument === null;
+    function hasParentMochaFunctionCall(functionExpression, options) {
+        return isTestCase(functionExpression.parent, options) || isHookCall(functionExpression.parent);
+    }
 
-    return isImplicitUndefined || isExplicitUndefined(argument);
-}
+    function isExplicitUndefined(node) {
+        return node && node.type === 'Identifier' && node.name === 'undefined';
+    }
 
-const findReturnStatement = find(propEq('type', 'ReturnStatement'));
+    function isReturnOfUndefined(node) {
+        const argument = node.argument;
+        const isImplicitUndefined = argument === null;
+
+        return isImplicitUndefined || isExplicitUndefined(argument);
+    }
+
+    return {
+        isDescribe,
+        isHookIdentifier,
+        isTestCase,
+        getPropertyName,
+        getNodeName,
+        isMochaFunctionCall,
+        isHookCall,
+        isSuiteConfigCall,
+        hasParentMochaFunctionCall,
+        findReturnStatement,
+        isReturnOfUndefined
+    };
+}
 
-module.exports = {
-    isDescribe,
-    isHookIdentifier,
-    isTestCase,
-    getPropertyName,
-    getNodeName,
-    isMochaFunctionCall,
-    isHookCall,
-    isSuiteConfigCall,
-    hasParentMochaFunctionCall,
-    findReturnStatement,
-    isReturnOfUndefined
-};
+module.exports = createAstUtils;
diff --git a/lib/util/names.js b/lib/util/names.js
index 1b41f27..f96fc57 100644
--- a/lib/util/names.js
+++ b/lib/util/names.js
@@ -1,74 +1,126 @@
 'use strict';
 
-const chain = require('ramda/src/chain');
+const where = require('ramda/src/where');
+const includes = require('ramda/src/includes');
+const intersection = require('ramda/src/intersection');
+const pipe = require('ramda/src/pipe');
+const isEmpty = require('ramda/src/isEmpty');
+const complement = require('ramda/src/complement');
+const flip = require('ramda/src/flip');
+const filter = require('ramda/src/filter');
+const over = require('ramda/src/over');
+const lensProp = require('ramda/src/lensProp');
+const map = require('ramda/src/map');
+const view = require('ramda/src/view');
+const assoc = require('ramda/src/assoc');
+const allPass = require('ramda/src/allPass');
 
-const suiteNames = [
-    'describe',
-    'context',
-    'suite'
-];
+const INTERFACES = {
+    BDD: 'BDD',
+    TDD: 'TDD',
+    QUnit: 'QUnit'
+};
 
-const suiteModifiers = {
-    skip: [
-        'describe.skip',
-        'context.skip',
-        'suite.skip',
-        'xdescribe',
-        'xcontext',
-        'xsuite'
-    ],
-    only: [
-        'describe.only',
-        'context.only',
-        'suite.only'
-    ]
+const TYPES = {
+    suite: 'suite',
+    testCase: 'testCase',
+    hook: 'hook'
 };
 
-const testCaseNames = [
-    'it',
-    'test',
-    'specify'
+const MODIFIERS = {
+    skip: 'skip',
+    only: 'only'
+};
+
+const baseNames = [
+    { name: 'describe', interfaces: [ INTERFACES.BDD ], type: TYPES.suite },
+    { name: 'context', interfaces: [ INTERFACES.BDD ], type: TYPES.suite },
+    { name: 'suite', interfaces: [ INTERFACES.TDD, INTERFACES.QUnit ], type: TYPES.suite },
+    { name: 'it', interfaces: [ INTERFACES.BDD ], type: TYPES.testCase },
+    { name: 'specify', interfaces: [ INTERFACES.BDD ], type: TYPES.testCase },
+    { name: 'test', interfaces: [ INTERFACES.TDD, INTERFACES.QUnit ], type: TYPES.testCase },
+    { name: 'before', interfaces: [ INTERFACES.BDD, INTERFACES.QUnit ], type: TYPES.hook },
+    { name: 'after', interfaces: [ INTERFACES.BDD, INTERFACES.QUnit ], type: TYPES.hook },
+    { name: 'beforeEach', interfaces: [ INTERFACES.BDD, INTERFACES.QUnit ], type: TYPES.hook },
+    { name: 'afterEach', interfaces: [ INTERFACES.BDD, INTERFACES.QUnit ], type: TYPES.hook },
+    { name: 'suiteSetup', interfaces: [ INTERFACES.TDD ], type: TYPES.hook },
+    { name: 'suiteTeardown', interfaces: [ INTERFACES.TDD ], type: TYPES.hook },
+    { name: 'setup', interfaces: [ INTERFACES.TDD ], type: TYPES.hook },
+    { name: 'teardown', interfaces: [ INTERFACES.TDD ], type: TYPES.hook }
 ];
 
-const testCaseModifiers = {
-    skip: [
-        'it.skip',
-        'test.skip',
-        'specify.skip',
-        'xit',
-        'xspecify'
-    ],
-    only: [
-        'it.only',
-        'test.only',
-        'specify.only'
-    ]
-};
+const includesSublist = (sublist) => pipe(intersection(sublist), complement(isEmpty));
+const isIncludedIn = flip(includes);
+const hasMatchingType = (typesToMatch) => where({ type: isIncludedIn(typesToMatch) });
+const hasMatchingInterfaces = (interfacesToMatch) => where({ interfaces: includesSublist(interfacesToMatch) });
+const hasMatchingModifier = (modifierToMatch) => where({ modifier: isIncludedIn(modifierToMatch) });
+const filterTestCasesAndSuites = filter(hasMatchingType([ TYPES.suite, TYPES.testCase ]));
 
-function getTestCaseNames(options = {}) {
-    const { modifiers = [], baseNames = true } = options;
-    const names = baseNames ? testCaseNames : [];
+const nameLens = lensProp('name');
+const mapNames = (fn) => map(over(nameLens, fn));
+const extractNames = map(view(nameLens));
+const addModifier = (modifier) => map(assoc('modifier', modifier));
 
-    return names.concat(chain((modifierName) => {
-        if (testCaseModifiers[modifierName]) {
-            return testCaseModifiers[modifierName];
-        }
+function formatXVariant(name) {
+    return `x${name}`;
+}
+
+function formatSkipVariant(name) {
+    return `${name}.${MODIFIERS.skip}`;
+}
 
-        return [];
-    }, modifiers));
+function formatExclusiveVariant(name) {
+    return `${name}.${MODIFIERS.only}`;
 }
 
-function getSuiteNames(options = {}) {
-    const { modifiers = [], baseNames = true, additionalSuiteNames = [] } = options;
-    const names = baseNames ? suiteNames.concat(additionalSuiteNames) : [];
+const buildXVariants = pipe(
+    filterTestCasesAndSuites,
+    filter(hasMatchingInterfaces([ INTERFACES.BDD ])),
+    mapNames(formatXVariant),
+    addModifier(MODIFIERS.skip)
+);
+
+const buildSkipVariants = pipe(
+    filterTestCasesAndSuites,
+    mapNames(formatSkipVariant),
+    addModifier(MODIFIERS.skip)
+);
 
-    return names.concat(chain((modifierName) => {
-        if (suiteModifiers[modifierName]) {
-            return suiteModifiers[modifierName];
-        }
+const buildExclusiveVariants = pipe(
+    filterTestCasesAndSuites,
+    mapNames(formatExclusiveVariant),
+    addModifier(MODIFIERS.only)
+);
+
+function buildAllNames(additionalNames) {
+    const names = addModifier(null)([ ...baseNames, ...additionalNames ]);
+
+    return [
+        ...names,
+        ...buildSkipVariants(names),
+        ...buildXVariants(names),
+        ...buildExclusiveVariants(names)
+    ];
+}
+
+function getNamesByType(type, filterOptions = {}) {
+    const { modifiers = [], modifiersOnly = false, additionalCustomNames = [] } = filterOptions;
+    const allNames = buildAllNames(additionalCustomNames);
+    const predicates = [
+        hasMatchingType([ type ]),
+        hasMatchingModifier([ ...modifiers, ...modifiersOnly ? [] : [ null ] ])
+    ];
+    const filteredNames = filter(allPass(predicates), allNames);
+
+    return extractNames(filteredNames);
+}
+
+function getTestCaseNames(options) {
+    return getNamesByType(TYPES.testCase, options);
+}
 
-        return [];
-    }, modifiers));
+function getSuiteNames(options) {
+    return getNamesByType(TYPES.suite, options);
 }
 
 module.exports = {
diff --git a/lib/util/settings.js b/lib/util/settings.js
index f1f03f7..52245eb 100644
--- a/lib/util/settings.js
+++ b/lib/util/settings.js
@@ -1,18 +1,15 @@
-/* eslint-env node*/
-
 'use strict';
 
-function settingFor(propertyName) {
-    return function (settings) {
-        const value = settings[`mocha/${ propertyName}`];
-        const mochaSettings = settings.mocha || {};
+function settingFor(settings, propertyName, fallback) {
+    const value = settings[`mocha/${propertyName}`];
+    const mochaSettings = settings.mocha || {};
 
-        return value || mochaSettings[propertyName] || [];
-    };
+    return value || mochaSettings[propertyName] || fallback;
 }
 
 module.exports = {
-    getAdditionalTestFunctions: settingFor('additionalTestFunctions'),
-    additionalSuiteNames: settingFor('additionalSuiteNames'),
-    getAdditionalXFunctions: settingFor('additionalXFunctions')
+    getAddtionalNames(settings) {
+        const additionalCustomNames = settingFor(settings, 'additionalCustomNames', []);
+        return additionalCustomNames;
+    }
 };


Test patch:
diff --git a/docs/rules/no-exclusive-tests.md b/docs/rules/no-exclusive-tests.md
index 3d1bb31..cb586b9 100644
--- a/docs/rules/no-exclusive-tests.md
+++ b/docs/rules/no-exclusive-tests.md
@@ -49,25 +49,6 @@ suite.skip("bar", function () {});
 test.skip("bar", function () {});
 ```
 
-## Settings
-
-This rule supports the following shared configuration setting:
-
-* `additionalTestFunctions`: An array of extra test functions to protect.  This might be used with a custom Mocha extension, such as [`ember-mocha`](https://github.com/switchfly/ember-mocha)
-
-```json
-{
-    "rules": {
-        "mocha/no-exclusive-tests": "error"
-    },
-    "settings": {
-       "mocha/additionalTestFunctions": [
-           "describeModule"
-       ]
-    }
-}
-```
-
 ## When Not To Use It
 
 * If you really want to execute only one test-suite or test-case because all other tests should not be executed, turn this rule off.
diff --git a/docs/rules/no-skipped-tests.md b/docs/rules/no-skipped-tests.md
index a78501a..cd63084 100644
--- a/docs/rules/no-skipped-tests.md
+++ b/docs/rules/no-skipped-tests.md
@@ -42,28 +42,6 @@ suite.only("bar", function () {});
 test.only("bar", function () {});
 ```
 
-## Settings
-
-This rule supports the following shared configuration settings:
-
-* `additionalTestFunctions`: An array of extra test functions to protect.  This might be used with a custom Mocha extension, such as [`ember-mocha`](https://github.com/switchfly/ember-mocha)
-* `additionalXFunctions`: An array of extra x-function to protect
-
-```json
-{
-    "rules": {
-        "mocha/no-skipped-tests": "error"
-    },
-    "settings": {
-       "mocha/additionalTestFunctions": [
-           "describeModule"
-       ],
-       "mocha/additionalXFunctions": [
-           "xdescribeModule"
-       ]
-    }
-}
-```
 ## When Not To Use It
 
 * If you really want to commit skipped tests to your repo, turn this rule off.
diff --git a/lib/rules/no-exclusive-tests.js b/lib/rules/no-exclusive-tests.js
index 739be8c..862bb05 100644
--- a/lib/rules/no-exclusive-tests.js
+++ b/lib/rules/no-exclusive-tests.js
@@ -1,7 +1,6 @@
 'use strict';
 
-const { getAdditionalTestFunctions } = require('../util/settings');
-const astUtils = require('../util/ast');
+const createAstUtils = require('../util/ast');
 
 module.exports = {
     meta: {
@@ -11,40 +10,15 @@ module.exports = {
         }
     },
     create(context) {
-        let mochaTestFunctions = [
-            'it',
-            'describe',
-            'suite',
-            'test',
-            'context',
-            'specify'
-        ];
-        const settings = context.settings;
-        const additionalTestFunctions = getAdditionalTestFunctions(settings);
-
-        mochaTestFunctions = mochaTestFunctions.concat(additionalTestFunctions);
-
-        function matchesMochaTestFunction(object) {
-            const name = astUtils.getNodeName(object);
-
-            return mochaTestFunctions.includes(name);
-        }
-
-        function isPropertyNamedOnly(property) {
-            return property && astUtils.getPropertyName(property) === 'only';
-        }
-
-        function isCallToMochasOnlyFunction(callee) {
-            return callee.type === 'MemberExpression' &&
-                matchesMochaTestFunction(callee.object) &&
-                isPropertyNamedOnly(callee.property);
-        }
+        const astUtils = createAstUtils(context.settings);
 
         return {
             CallExpression(node) {
-                const callee = node.callee;
+                const options = { modifiers: [ 'only' ], modifiersOnly: true };
+
+                if (astUtils.isDescribe(node, options) || astUtils.isTestCase(node, options)) {
+                    const callee = node.callee;
 
-                if (callee && isCallToMochasOnlyFunction(callee)) {
                     context.report({
                         node: callee.property,
                         message: 'Unexpected exclusive mocha test.'
diff --git a/lib/rules/no-global-tests.js b/lib/rules/no-global-tests.js
index 441f006..fc15674 100644
--- a/lib/rules/no-global-tests.js
+++ b/lib/rules/no-global-tests.js
@@ -1,6 +1,6 @@
 'use strict';
 
-const astUtils = require('../util/ast');
+const createAstUtils = require('../util/ast');
 
 module.exports = {
     meta: {
@@ -10,6 +10,8 @@ module.exports = {
         }
     },
     create(context) {
+        const astUtils = createAstUtils(context.settings);
+
         function isGlobalScope(scope) {
             return scope.type === 'global' || scope.type === 'module';
         }
diff --git a/lib/rules/no-nested-tests.js b/lib/rules/no-nested-tests.js
index 439e2f8..089bfdf 100644
--- a/lib/rules/no-nested-tests.js
+++ b/lib/rules/no-nested-tests.js
@@ -2,8 +2,7 @@
 
 /* eslint "complexity": [ "error", 5 ] */
 
-const astUtils = require('../util/ast');
-const { additionalSuiteNames } = require('../util/settings');
+const createAstUtils = require('../util/ast');
 
 module.exports = {
     meta: {
@@ -13,7 +12,7 @@ module.exports = {
         }
     },
     create(context) {
-        const settings = context.settings;
+        const astUtils = createAstUtils(context.settings);
         let testNestingLevel = 0;
         let hookCallNestingLevel = 0;
 
@@ -49,7 +48,7 @@ module.exports = {
             CallExpression(node) {
                 const isTestCase = astUtils.isTestCase(node);
                 const isHookCall = astUtils.isHookCall(node);
-                const isDescribe = astUtils.isDescribe(node, additionalSuiteNames(settings));
+                const isDescribe = astUtils.isDescribe(node);
 
                 checkForAndReportErrors(node, isTestCase, isDescribe, isHookCall);
 
diff --git a/lib/rules/no-pending-tests.js b/lib/rules/no-pending-tests.js
index 54b3248..e9c182e 100644
--- a/lib/rules/no-pending-tests.js
+++ b/lib/rules/no-pending-tests.js
@@ -1,6 +1,6 @@
 'use strict';
 
-const astUtils = require('../util/ast');
+const createAstUtils = require('../util/ast');
 
 module.exports = {
     meta: {
@@ -10,6 +10,8 @@ module.exports = {
         }
     },
     create(context) {
+        const astUtils = createAstUtils(context.settings);
+
         function isPendingMochaTest(node) {
             return astUtils.isTestCase(node) &&
                 node.arguments.length === 1 &&
diff --git a/lib/rules/no-skipped-tests.js b/lib/rules/no-skipped-tests.js
index 4ca8847..34edd70 100644
--- a/lib/rules/no-skipped-tests.js
+++ b/lib/rules/no-skipped-tests.js
@@ -1,31 +1,6 @@
 'use strict';
 
-const { getAdditionalTestFunctions, getAdditionalXFunctions } = require('../util/settings');
-
-let mochaTestFunctions;
-let mochaXFunctions;
-
-function matchesMochaTestFunction(object) {
-    return object && mochaTestFunctions.includes(object.name);
-}
-
-function isPropertyNamedSkip(property) {
-    return property && (property.name === 'skip' || property.value === 'skip');
-}
-
-function isCallToMochasSkipFunction(callee) {
-    return callee.type === 'MemberExpression' &&
-       matchesMochaTestFunction(callee.object) &&
-       isPropertyNamedSkip(callee.property);
-}
-
-function isMochaXFunction(name) {
-    return mochaXFunctions.includes(name);
-}
-
-function isCallToMochaXFunction(callee) {
-    return callee.type === 'Identifier' && isMochaXFunction(callee.name);
-}
+const createAstUtils = require('../util/ast');
 
 module.exports = {
     meta: {
@@ -35,37 +10,18 @@ module.exports = {
         type: 'problem'
     },
     create(context) {
-        const settings = context.settings;
-        const additionalTestFunctions = getAdditionalTestFunctions(settings);
-        const additionalXFunctions = getAdditionalXFunctions(settings);
-
-        mochaTestFunctions = [
-            'it',
-            'describe',
-            'suite',
-            'test',
-            'context',
-            'specify'
-        ].concat(additionalTestFunctions);
-        mochaXFunctions = [
-            'xit',
-            'xdescribe',
-            'xcontext',
-            'xspecify'
-        ].concat(additionalXFunctions);
+        const astUtils = createAstUtils(context.settings);
 
         return {
             CallExpression(node) {
-                const callee = node.callee;
+                const options = { modifiers: [ 'skip' ], modifiersOnly: true };
+
+                if (astUtils.isDescribe(node, options) || astUtils.isTestCase(node, options)) {
+                    const callee = node.callee;
+                    const nodeToReport = callee.type === 'MemberExpression' ? callee.property : callee;
 
-                if (isCallToMochasSkipFunction(callee)) {
-                    context.report({
-                        node: callee.property,
-                        message: 'Unexpected skipped mocha test.'
-                    });
-                } else if (isCallToMochaXFunction(callee)) {
                     context.report({
-                        node: callee,
+                        node: nodeToReport,
                         message: 'Unexpected skipped mocha test.'
                     });
                 }
diff --git a/lib/rules/no-synchronous-tests.js b/lib/rules/no-synchronous-tests.js
index 3f0bac9..9bd0559 100644
--- a/lib/rules/no-synchronous-tests.js
+++ b/lib/rules/no-synchronous-tests.js
@@ -2,7 +2,7 @@
 
 const isNil = require('ramda/src/isNil');
 const find = require('ramda/src/find');
-const astUtil = require('../util/ast');
+const createAstUtils = require('../util/ast');
 
 const asyncMethods = [ 'async', 'callback', 'promise' ];
 
@@ -59,11 +59,12 @@ module.exports = {
         ]
     },
     create(context) {
+        const astUtils = createAstUtils(context.settings);
         const options = context.options[0] || {};
         const allowedAsyncMethods = isNil(options.allowed) ? asyncMethods : options.allowed;
 
         function check(node) {
-            if (astUtil.hasParentMochaFunctionCall(node)) {
+            if (astUtils.hasParentMochaFunctionCall(node)) {
                 // For each allowed async test method, check if it is used in the test
                 const testAsyncMethods = allowedAsyncMethods.map(function (method) {
                     switch (method) {
diff --git a/test/rules/max-top-level-suites.js b/test/rules/max-top-level-suites.js
index 6ce54df..fe41c39 100644
--- a/test/rules/max-top-level-suites.js
+++ b/test/rules/max-top-level-suites.js
@@ -43,13 +43,13 @@ ruleTester.run('max-top-level-suites', rules['max-top-level-suites'], {
         {
             code: 'foo("This is a test", function () { });',
             settings: {
-                'mocha/additionalSuiteNames': [ 'foo' ]
+                'mocha/additionalCustomNames': [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
             }
         }, {
             code: 'foo("This is a test", function () { });',
             settings: {
                 mocha: {
-                    additionalSuiteNames: [ 'foo' ]
+                    additionalCustomNames: [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
                 }
             }
         },
@@ -195,7 +195,7 @@ ruleTester.run('max-top-level-suites', rules['max-top-level-suites'], {
             code: 'foo("this is a test", function () { });' +
                   'foo("this is a different test", function () { });',
             settings: {
-                'mocha/additionalSuiteNames': [ 'foo' ]
+                'mocha/additionalCustomNames': [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
             },
             errors: [
                 { message: 'The number of top-level suites is more than 1.' }
@@ -205,7 +205,7 @@ ruleTester.run('max-top-level-suites', rules['max-top-level-suites'], {
                   'foo("this is a different test", function () { });',
             settings: {
                 mocha: {
-                    additionalSuiteNames: [ 'foo' ]
+                    additionalCustomNames: [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
                 }
             },
             errors: [
diff --git a/test/rules/no-async-describe.js b/test/rules/no-async-describe.js
index d8ef7ab..bcb16c2 100644
--- a/test/rules/no-async-describe.js
+++ b/test/rules/no-async-describe.js
@@ -32,7 +32,7 @@ ruleTester.run('no-async-describe', rule, {
             output: 'foo("hello", function () {})',
             settings: {
                 mocha: {
-                    additionalSuiteNames: [ 'foo' ]
+                    additionalCustomNames: [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
                 }
             },
             parserOptions: { ecmaVersion: 8 }, errors: [ {
diff --git a/test/rules/no-exclusive-tests.js b/test/rules/no-exclusive-tests.js
index 4efacf7..eee1ab1 100644
--- a/test/rules/no-exclusive-tests.js
+++ b/test/rules/no-exclusive-tests.js
@@ -27,7 +27,7 @@ ruleTester.run('no-exclusive-tests', rules['no-exclusive-tests'], {
             code: 'a.b.c.skip()',
             settings: {
                 mocha: {
-                    additionalTestFunctions: [ 'a.b.c' ]
+                    additionalCustomNames: [ { name: 'a.b.c', type: 'testCase', interfaces: [ 'BDD' ] } ]
                 }
             }
         },
@@ -35,7 +35,7 @@ ruleTester.run('no-exclusive-tests', rules['no-exclusive-tests'], {
             code: 'a[b].c.skip()',
             settings: {
                 mocha: {
-                    additionalTestFunctions: [ 'a.b.c' ]
+                    additionalCustomNames: [ { name: 'a.b.c', type: 'testCase', interfaces: [ 'BDD' ] } ]
                 }
             }
         }
@@ -93,14 +93,14 @@ ruleTester.run('no-exclusive-tests', rules['no-exclusive-tests'], {
         {
             code: 'custom.only()',
             settings: {
-                'mocha/additionalTestFunctions': [ 'custom' ]
+                'mocha/additionalCustomNames': [ { name: 'custom', type: 'testCase', interfaces: [ 'BDD' ] } ]
             },
             errors: [ { message: expectedErrorMessage, column: 8, line: 1 } ]
         },
         {
             code: 'custom["only"]()',
             settings: {
-                'mocha/additionalTestFunctions': [ 'custom' ]
+                'mocha/additionalCustomNames': [ { name: 'custom', type: 'testCase', interfaces: [ 'BDD' ] } ]
             },
             errors: [ { message: expectedErrorMessage, column: 8, line: 1 } ]
         },
@@ -108,7 +108,7 @@ ruleTester.run('no-exclusive-tests', rules['no-exclusive-tests'], {
             code: 'custom.only()',
             settings: {
                 mocha: {
-                    additionalTestFunctions: [ 'custom' ]
+                    additionalCustomNames: [ { name: 'custom', type: 'testCase', interfaces: [ 'BDD' ] } ]
                 }
             },
             errors: [ { message: expectedErrorMessage, column: 8, line: 1 } ]
@@ -117,7 +117,7 @@ ruleTester.run('no-exclusive-tests', rules['no-exclusive-tests'], {
             code: 'custom["only"]()',
             settings: {
                 mocha: {
-                    additionalTestFunctions: [ 'custom' ]
+                    additionalCustomNames: [ { name: 'custom', type: 'testCase', interfaces: [ 'BDD' ] } ]
                 }
             },
             errors: [ { message: expectedErrorMessage, column: 8, line: 1 } ]
@@ -126,7 +126,7 @@ ruleTester.run('no-exclusive-tests', rules['no-exclusive-tests'], {
             code: 'foo.bar.only()',
             settings: {
                 mocha: {
-                    additionalTestFunctions: [ 'foo.bar' ]
+                    additionalCustomNames: [ { name: 'foo.bar', type: 'testCase', interfaces: [ 'BDD' ] } ]
                 }
             },
             errors: [ { message: expectedErrorMessage, column: 9, line: 1 } ]
@@ -135,7 +135,7 @@ ruleTester.run('no-exclusive-tests', rules['no-exclusive-tests'], {
             code: 'foo.bar["only"]()',
             settings: {
                 mocha: {
-                    additionalTestFunctions: [ 'foo.bar' ]
+                    additionalCustomNames: [ { name: 'foo.bar', type: 'testCase', interfaces: [ 'BDD' ] } ]
                 }
             },
             errors: [ { message: expectedErrorMessage, column: 9, line: 1 } ]
@@ -144,7 +144,7 @@ ruleTester.run('no-exclusive-tests', rules['no-exclusive-tests'], {
             code: 'foo["bar"].only()',
             settings: {
                 mocha: {
-                    additionalTestFunctions: [ 'foo.bar' ]
+                    additionalCustomNames: [ { name: 'foo.bar', type: 'testCase', interfaces: [ 'BDD' ] } ]
                 }
             },
             errors: [ { message: expectedErrorMessage, column: 12, line: 1 } ]
@@ -153,7 +153,7 @@ ruleTester.run('no-exclusive-tests', rules['no-exclusive-tests'], {
             code: 'foo["bar"]["only"]()',
             settings: {
                 mocha: {
-                    additionalTestFunctions: [ 'foo.bar' ]
+                    additionalCustomNames: [ { name: 'foo.bar', type: 'testCase', interfaces: [ 'BDD' ] } ]
                 }
             },
             errors: [ { message: expectedErrorMessage, column: 12, line: 1 } ]
@@ -162,7 +162,7 @@ ruleTester.run('no-exclusive-tests', rules['no-exclusive-tests'], {
             code: 'a.b.c.only()',
             settings: {
                 mocha: {
-                    additionalTestFunctions: [ 'a.b.c' ]
+                    additionalCustomNames: [ { name: 'a.b.c', type: 'testCase', interfaces: [ 'BDD' ] } ]
                 }
             },
             errors: [ { message: expectedErrorMessage, column: 7, line: 1 } ]
diff --git a/test/rules/no-hooks-for-single-case.js b/test/rules/no-hooks-for-single-case.js
index 6eeb677..48abafa 100644
--- a/test/rules/no-hooks-for-single-case.js
+++ b/test/rules/no-hooks-for-single-case.js
@@ -153,7 +153,7 @@ ruleTester.run('no-hooks-for-single-case', rules['no-hooks-for-single-case'], {
             ].join('\n'),
             settings: {
                 mocha: {
-                    additionalSuiteNames: [ 'foo' ]
+                    additionalCustomNames: [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
                 }
             }
         },
@@ -166,7 +166,7 @@ ruleTester.run('no-hooks-for-single-case', rules['no-hooks-for-single-case'], {
                 '});'
             ].join('\n'),
             settings: {
-                'mocha/additionalSuiteNames': [ 'foo' ]
+                'mocha/additionalCustomNames': [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
             }
         }
     ],
@@ -294,7 +294,7 @@ ruleTester.run('no-hooks-for-single-case', rules['no-hooks-for-single-case'], {
             ].join('\n'),
             settings: {
                 mocha: {
-                    additionalSuiteNames: [ 'foo' ]
+                    additionalCustomNames: [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
                 }
             },
             errors: [ { message: 'Unexpected use of Mocha `before` hook for a single test case', column: 5, line: 2 } ]
@@ -306,7 +306,7 @@ ruleTester.run('no-hooks-for-single-case', rules['no-hooks-for-single-case'], {
                 '});'
             ].join('\n'),
             settings: {
-                'mocha/additionalSuiteNames': [ 'foo' ]
+                'mocha/additionalCustomNames': [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
             },
             errors: [ { message: 'Unexpected use of Mocha `before` hook for a single test case', column: 5, line: 2 } ]
         }
diff --git a/test/rules/no-identical-title.js b/test/rules/no-identical-title.js
index 43e0fde..4a8f2dd 100644
--- a/test/rules/no-identical-title.js
+++ b/test/rules/no-identical-title.js
@@ -98,7 +98,7 @@ ruleTester.run('no-identical-title', rules['no-identical-title'], {
                 'foo("describe2", function() {});'
             ].join('\n'),
             settings: {
-                'mocha/additionalSuiteNames': [ 'foo' ]
+                'mocha/additionalCustomNames': [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
             }
         }, {
             code: [
@@ -107,7 +107,7 @@ ruleTester.run('no-identical-title', rules['no-identical-title'], {
             ].join('\n'),
             settings: {
                 mocha: {
-                    additionalSuiteNames: [ 'foo' ]
+                    additionalCustomNames: [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
                 }
             }
         }
@@ -180,7 +180,7 @@ ruleTester.run('no-identical-title', rules['no-identical-title'], {
                 'foo("describe1", function() {});'
             ].join('\n'),
             settings: {
-                'mocha/additionalSuiteNames': [ 'foo' ]
+                'mocha/additionalCustomNames': [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
             },
             errors: [ { message: 'Test suite title is used multiple times.', column: 1, line: 2 } ]
         }, {
@@ -190,7 +190,7 @@ ruleTester.run('no-identical-title', rules['no-identical-title'], {
             ].join('\n'),
             settings: {
                 mocha: {
-                    additionalSuiteNames: [ 'foo' ]
+                    additionalCustomNames: [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
                 }
             },
             errors: [ { message: 'Test suite title is used multiple times.', column: 1, line: 2 } ]
diff --git a/test/rules/no-nested-tests.js b/test/rules/no-nested-tests.js
index 98f7b41..6a912da 100644
--- a/test/rules/no-nested-tests.js
+++ b/test/rules/no-nested-tests.js
@@ -13,13 +13,13 @@ ruleTester.run('no-nested-tests', rule, {
         {
             code: 'foo("", function () { it(); })',
             settings: {
-                'mocha/additionalSuiteNames': [ 'foo' ]
+                'mocha/additionalCustomNames': [ { name: 'foo', interfaces: [ 'BDD' ] } ]
             }
         }, {
             code: 'foo("", function () { it(); })',
             settings: {
                 mocha: {
-                    additionalSuiteNames: [ 'foo' ]
+                    additionalCustomNames: [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
                 }
             }
         }
@@ -121,7 +121,7 @@ ruleTester.run('no-nested-tests', rule, {
         {
             code: 'it("", function () { foo() });',
             settings: {
-                'mocha/additionalSuiteNames': [ 'foo' ]
+                'mocha/additionalCustomNames': [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
             },
             errors: [ {
                 message: 'Unexpected suite nested within a test.',
@@ -133,7 +133,7 @@ ruleTester.run('no-nested-tests', rule, {
             code: 'it("", function () { foo() });',
             settings: {
                 mocha: {
-                    additionalSuiteNames: [ 'foo' ]
+                    additionalCustomNames: [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
                 }
             },
             errors: [ {
diff --git a/test/rules/no-setup-in-describe.js b/test/rules/no-setup-in-describe.js
index 63139e5..cd13e47 100644
--- a/test/rules/no-setup-in-describe.js
+++ b/test/rules/no-setup-in-describe.js
@@ -56,20 +56,20 @@ ruleTester.run('no-setup-in-describe', rule, {
         {
             code: 'foo("", function () { it(); })',
             settings: {
-                'mocha/additionalSuiteNames': [ 'foo' ]
+                'mocha/additionalCustomNames': [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
             }
         }, {
             code: 'foo("", function () { it(); })',
             settings: {
                 mocha: {
-                    additionalSuiteNames: [ 'foo' ]
+                    additionalCustomNames: [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
                 }
             }
         }, {
             code: 'foo("", function () { it("", function () { b(); }); })',
             settings: {
                 mocha: {
-                    additionalSuiteNames: [ 'foo' ]
+                    additionalCustomNames: [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
                 }
             }
         },
@@ -112,7 +112,7 @@ ruleTester.run('no-setup-in-describe', rule, {
             code: 'foo("", function () { a(); });',
             settings: {
                 mocha: {
-                    additionalSuiteNames: [ 'foo' ]
+                    additionalCustomNames: [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
                 }
             },
             errors: [ {
@@ -124,7 +124,7 @@ ruleTester.run('no-setup-in-describe', rule, {
             code: 'foo("", function () { a[b]; });',
             settings: {
                 mocha: {
-                    additionalSuiteNames: [ 'foo' ]
+                    additionalCustomNames: [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
                 }
             },
             errors: [ {
@@ -136,7 +136,7 @@ ruleTester.run('no-setup-in-describe', rule, {
             code: 'foo("", function () { a["b"]; });',
             settings: {
                 mocha: {
-                    additionalSuiteNames: [ 'foo' ]
+                    additionalCustomNames: [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
                 }
             },
             errors: [ {
@@ -167,7 +167,7 @@ ruleTester.run('no-setup-in-describe', rule, {
             code: 'foo("", function () { a.b; });',
             settings: {
                 mocha: {
-                    additionalSuiteNames: [ 'foo' ]
+                    additionalCustomNames: [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
                 }
             },
             errors: [ {
diff --git a/test/rules/no-sibling-hooks.js b/test/rules/no-sibling-hooks.js
index 198d253..3c62fa0 100644
--- a/test/rules/no-sibling-hooks.js
+++ b/test/rules/no-sibling-hooks.js
@@ -82,7 +82,7 @@ ruleTester.run('no-sibling-hooks', rules['no-sibling-hooks'], {
                 '});'
             ].join('\n'),
             settings: {
-                'mocha/additionalSuiteNames': [ 'foo' ]
+                'mocha/additionalCustomNames': [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
             }
         }, {
             code: [
@@ -95,7 +95,7 @@ ruleTester.run('no-sibling-hooks', rules['no-sibling-hooks'], {
             ].join('\n'),
             settings: {
                 mocha: {
-                    additionalSuiteNames: [ 'foo' ]
+                    additionalCustomNames: [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
                 }
             }
         }
@@ -153,7 +153,7 @@ ruleTester.run('no-sibling-hooks', rules['no-sibling-hooks'], {
                 '});'
             ].join('\n'),
             settings: {
-                'mocha/additionalSuiteNames': [ 'foo' ]
+                'mocha/additionalCustomNames': [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
             },
             errors: [ { message: 'Unexpected use of duplicate Mocha `before` hook', column: 5, line: 6 } ]
         }, {
@@ -168,7 +168,7 @@ ruleTester.run('no-sibling-hooks', rules['no-sibling-hooks'], {
             ].join('\n'),
             settings: {
                 mocha: {
-                    additionalSuiteNames: [ 'foo' ]
+                    additionalCustomNames: [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
                 }
             },
             errors: [ { message: 'Unexpected use of duplicate Mocha `before` hook', column: 5, line: 6 } ]
diff --git a/test/rules/no-skipped-tests.js b/test/rules/no-skipped-tests.js
index 91bf89c..7655c5e 100644
--- a/test/rules/no-skipped-tests.js
+++ b/test/rules/no-skipped-tests.js
@@ -20,7 +20,15 @@ ruleTester.run('no-skipped-tests', rules['no-skipped-tests'], {
         'context.only()',
         'var appliedOnly = describe.skip; appliedOnly.apply(describe)',
         'var calledOnly = it.skip; calledOnly.call(it)',
-        'var dynamicOnly = "skip"; suite[dynamicOnly]()'
+        'var dynamicOnly = "skip"; suite[dynamicOnly]()',
+        {
+            code: 'xcustom()',
+            settings: {
+                mocha: {
+                    additionalCustomNames: [ { name: 'custom', type: 'testCase', interfaces: [ 'TDD' ] } ]
+                }
+            }
+        }
     ],
 
     invalid: [
@@ -87,21 +95,21 @@ ruleTester.run('no-skipped-tests', rules['no-skipped-tests'], {
         {
             code: 'custom.skip()',
             settings: {
-                'mocha/additionalTestFunctions': [ 'custom' ]
+                'mocha/additionalCustomNames': [ { name: 'custom', type: 'testCase', interfaces: [ 'BDD' ] } ]
             },
             errors: [ { message: expectedErrorMessage, column: 8, line: 1 } ]
         },
         {
             code: 'custom["skip"]()',
             settings: {
-                'mocha/additionalTestFunctions': [ 'custom' ]
+                'mocha/additionalCustomNames': [ { name: 'custom', type: 'testCase', interfaces: [ 'BDD' ] } ]
             },
             errors: [ { message: expectedErrorMessage, column: 8, line: 1 } ]
         },
         {
             code: 'xcustom()',
             settings: {
-                'mocha/additionalXFunctions': [ 'xcustom' ]
+                'mocha/additionalCustomNames': [ { name: 'custom', type: 'testCase', interfaces: [ 'BDD' ] } ]
             },
             errors: [ { message: expectedErrorMessage, column: 1, line: 1 } ]
         },
@@ -109,7 +117,7 @@ ruleTester.run('no-skipped-tests', rules['no-skipped-tests'], {
             code: 'custom.skip()',
             settings: {
                 mocha: {
-                    additionalTestFunctions: [ 'custom' ]
+                    additionalCustomNames: [ { name: 'custom', type: 'testCase', interfaces: [ 'BDD' ] } ]
                 }
             },
             errors: [ { message: expectedErrorMessage, column: 8, line: 1 } ]
@@ -118,7 +126,7 @@ ruleTester.run('no-skipped-tests', rules['no-skipped-tests'], {
             code: 'custom["skip"]()',
             settings: {
                 mocha: {
-                    additionalTestFunctions: [ 'custom' ]
+                    additionalCustomNames: [ { name: 'custom', type: 'testCase', interfaces: [ 'BDD' ] } ]
                 }
             },
             errors: [ { message: expectedErrorMessage, column: 8, line: 1 } ]
@@ -127,12 +135,11 @@ ruleTester.run('no-skipped-tests', rules['no-skipped-tests'], {
             code: 'xcustom()',
             settings: {
                 mocha: {
-                    additionalXFunctions: [ 'xcustom' ]
+                    additionalCustomNames: [ { name: 'custom', type: 'testCase', interfaces: [ 'BDD' ] } ]
                 }
             },
             errors: [ { message: expectedErrorMessage, column: 1, line: 1 } ]
         }
-
     ]
 
 });
diff --git a/test/rules/no-top-level-hooks.js b/test/rules/no-top-level-hooks.js
index a7c4a07..b1d76f2 100644
--- a/test/rules/no-top-level-hooks.js
+++ b/test/rules/no-top-level-hooks.js
@@ -25,13 +25,13 @@ ruleTester.run('no-top-level-hooks', rules['no-top-level-hooks'], {
         {
             code: 'foo(function() { before(function() {}); });',
             settings: {
-                'mocha/additionalSuiteNames': [ 'foo' ]
+                'mocha/additionalCustomNames': [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
             }
         }, {
             code: 'foo(function() { before(function() {}); });',
             settings: {
                 mocha: {
-                    additionalSuiteNames: [ 'foo' ]
+                    additionalCustomNames: [ { name: 'foo', type: 'suite', interfaces: [ 'BDD' ] } ]
                 }
             }
         }
diff --git a/test/rules/prefer-arrow-callback.js b/test/rules/prefer-arrow-callback.js
index 4264d91..f18ffba 100644
--- a/test/rules/prefer-arrow-callback.js
+++ b/test/rules/prefer-arrow-callback.js
@@ -57,7 +57,6 @@ ruleTester.run('prefer-arrow-callback', rules['prefer-arrow-callback'], {
         'context.only("name", function bar() {});',
         'context.skip("name", function bar() {});',
         'suite("name", function bar() {});',
-        'xsuite("name", function bar() {});',
         'suite.only("name", function bar() {});',
         'suite.skip("name", function bar() {});',
         'it("name", function bar() {});',
diff --git a/test/util/namesSpec.js b/test/util/namesSpec.js
index 545c01c..5275ff5 100644
--- a/test/util/namesSpec.js
+++ b/test/util/namesSpec.js
@@ -10,27 +10,27 @@ describe('mocha names', () => {
 
             expect(testCaseNames).to.deep.equal([
                 'it',
-                'test',
-                'specify'
+                'specify',
+                'test'
             ]);
         });
 
         it('returns an empty list when no modifiers and no base names are wanted', () => {
-            const testCaseNames = getTestCaseNames({ baseNames: false });
+            const testCaseNames = getTestCaseNames({ modifiersOnly: true, modifiers: [] });
 
             expect(testCaseNames).to.deep.equal([]);
         });
 
         it('always returns a new array', () => {
-            const testCaseNames1 = getTestCaseNames({ baseNames: false });
-            const testCaseNames2 = getTestCaseNames({ baseNames: false });
+            const testCaseNames1 = getTestCaseNames({ modifiersOnly: true });
+            const testCaseNames2 = getTestCaseNames({ modifiersOnly: true });
 
             expect(testCaseNames1).to.deep.equal(testCaseNames2);
             expect(testCaseNames1).to.not.equal(testCaseNames2);
         });
 
         it('ignores invalid modifiers', () => {
-            const testCaseNames = getTestCaseNames({ modifiers: [ 'foo' ], baseNames: false });
+            const testCaseNames = getTestCaseNames({ modifiers: [ 'foo' ], modifiersOnly: true });
 
             expect(testCaseNames).to.deep.equal([]);
         });
@@ -40,23 +40,23 @@ describe('mocha names', () => {
 
             expect(testCaseNames).to.deep.equal([
                 'it',
-                'test',
                 'specify',
+                'test',
                 'it.skip',
-                'test.skip',
                 'specify.skip',
+                'test.skip',
                 'xit',
                 'xspecify'
             ]);
         });
 
         it('returns the list of test case names only with "skip" modifiers applied', () => {
-            const testCaseNames = getTestCaseNames({ modifiers: [ 'skip' ], baseNames: false });
+            const testCaseNames = getTestCaseNames({ modifiers: [ 'skip' ], modifiersOnly: true });
 
             expect(testCaseNames).to.deep.equal([
                 'it.skip',
-                'test.skip',
                 'specify.skip',
+                'test.skip',
                 'xit',
                 'xspecify'
             ]);
@@ -67,21 +67,21 @@ describe('mocha names', () => {
 
             expect(testCaseNames).to.deep.equal([
                 'it',
-                'test',
                 'specify',
+                'test',
                 'it.only',
-                'test.only',
-                'specify.only'
+                'specify.only',
+                'test.only'
             ]);
         });
 
         it('returns the list of test case names only with "only" modifiers applied', () => {
-            const testCaseNames = getTestCaseNames({ modifiers: [ 'only' ], baseNames: false });
+            const testCaseNames = getTestCaseNames({ modifiers: [ 'only' ], modifiersOnly: true });
 
             expect(testCaseNames).to.deep.equal([
                 'it.only',
-                'test.only',
-                'specify.only'
+                'specify.only',
+                'test.only'
             ]);
         });
 
@@ -90,31 +90,80 @@ describe('mocha names', () => {
 
             expect(testCaseNames).to.deep.equal([
                 'it',
-                'test',
                 'specify',
+                'test',
                 'it.skip',
-                'test.skip',
                 'specify.skip',
+                'test.skip',
                 'xit',
                 'xspecify',
                 'it.only',
-                'test.only',
-                'specify.only'
+                'specify.only',
+                'test.only'
             ]);
         });
 
         it('returns the list of test case names only with modifiers applied', () => {
-            const testCaseNames = getTestCaseNames({ modifiers: [ 'skip', 'only' ], baseNames: false });
+            const testCaseNames = getTestCaseNames({ modifiers: [ 'skip', 'only' ], modifiersOnly: true });
 
             expect(testCaseNames).to.deep.equal([
                 'it.skip',
-                'test.skip',
                 'specify.skip',
+                'test.skip',
                 'xit',
                 'xspecify',
                 'it.only',
-                'test.only',
-                'specify.only'
+                'specify.only',
+                'test.only'
+            ]);
+        });
+
+        it('returns the additional test case names', () => {
+            const testCaseNames = getTestCaseNames({
+                additionalCustomNames: [
+                    { name: 'myCustomIt', type: 'testCase', interfaces: [ 'BDD' ] }
+                ]
+            });
+
+            expect(testCaseNames).to.deep.equal([
+                'it',
+                'specify',
+                'test',
+                'myCustomIt'
+            ]);
+        });
+
+        it('doesn’t return the additional suite names when base names shouldn’t be included', () => {
+            const testCaseNames = getTestCaseNames({
+                additionalCustomNames: [
+                    { name: 'myCustomIt', type: 'testCase', interfaces: [ 'BDD' ] }
+                ],
+                modifiersOnly: true
+            });
+
+            expect(testCaseNames).to.deep.equal([]);
+        });
+
+        it('returns the additional skip modifiers', () => {
+            const testCaseNames = getTestCaseNames({
+                additionalCustomNames: [
+                    { name: 'myCustomIt', type: 'testCase', interfaces: [ 'BDD' ] },
+                    { name: 'myCustomTest', type: 'testCase', interfaces: [ 'TDD' ] }
+
+                ],
+                modifiersOnly: true,
+                modifiers: [ 'skip' ]
+            });
+
+            expect(testCaseNames).to.deep.equal([
+                'it.skip',
+                'specify.skip',
+                'test.skip',
+                'myCustomIt.skip',
+                'myCustomTest.skip',
+                'xit',
+                'xspecify',
+                'xmyCustomIt'
             ]);
         });
     });
@@ -131,21 +180,21 @@ describe('mocha names', () => {
         });
 
         it('returns an empty list when no modifiers and no base names are wanted', () => {
-            const suiteNames = getSuiteNames({ baseNames: false });
+            const suiteNames = getSuiteNames({ modifiersOnly: true });
 
             expect(suiteNames).to.deep.equal([]);
         });
 
         it('always returns a new array', () => {
-            const suiteNames1 = getSuiteNames({ baseNames: false });
-            const suiteNames2 = getSuiteNames({ baseNames: false });
+            const suiteNames1 = getSuiteNames({ modifiersOnly: true });
+            const suiteNames2 = getSuiteNames({ modifiersOnly: true });
 
             expect(suiteNames1).to.deep.equal(suiteNames2);
             expect(suiteNames1).to.not.equal(suiteNames2);
         });
 
         it('ignores invalid modifiers', () => {
-            const suiteNames = getSuiteNames({ modifiers: [ 'foo' ], baseNames: false });
+            const suiteNames = getSuiteNames({ modifiers: [ 'foo' ], modifiersOnly: true });
 
             expect(suiteNames).to.deep.equal([]);
         });
@@ -161,21 +210,19 @@ describe('mocha names', () => {
                 'context.skip',
                 'suite.skip',
                 'xdescribe',
-                'xcontext',
-                'xsuite'
+                'xcontext'
             ]);
         });
 
         it('returns the list of suite names only with "skip" modifiers applied', () => {
-            const suiteNames = getSuiteNames({ modifiers: [ 'skip' ], baseNames: false });
+            const suiteNames = getSuiteNames({ modifiers: [ 'skip' ], modifiersOnly: true });
 
             expect(suiteNames).to.deep.equal([
                 'describe.skip',
                 'context.skip',
                 'suite.skip',
                 'xdescribe',
-                'xcontext',
-                'xsuite'
+                'xcontext'
             ]);
         });
 
@@ -193,7 +240,7 @@ describe('mocha names', () => {
         });
 
         it('returns the list of suite names only with "only" modifiers applied', () => {
-            const suiteNames = getSuiteNames({ modifiers: [ 'only' ], baseNames: false });
+            const suiteNames = getSuiteNames({ modifiers: [ 'only' ], modifiersOnly: true });
 
             expect(suiteNames).to.deep.equal([
                 'describe.only',
@@ -214,15 +261,14 @@ describe('mocha names', () => {
                 'suite.skip',
                 'xdescribe',
                 'xcontext',
-                'xsuite',
                 'describe.only',
                 'context.only',
                 'suite.only'
             ]);
         });
 
-        it('returns the list of suite names names only with modifiers applied', () => {
-            const suiteNames = getSuiteNames({ modifiers: [ 'skip', 'only' ], baseNames: false });
+        it('returns the list of suite names only with modifiers applied', () => {
+            const suiteNames = getSuiteNames({ modifiers: [ 'skip', 'only' ], modifiersOnly: true });
 
             expect(suiteNames).to.deep.equal([
                 'describe.skip',
@@ -230,7 +276,6 @@ describe('mocha names', () => {
                 'suite.skip',
                 'xdescribe',
                 'xcontext',
-                'xsuite',
                 'describe.only',
                 'context.only',
                 'suite.only'
@@ -238,7 +283,11 @@ describe('mocha names', () => {
         });
 
         it('returns the additional suite names', () => {
-            const suiteNames = getSuiteNames({ additionalSuiteNames: [ 'myCustomDescribe' ] });
+            const suiteNames = getSuiteNames({
+                additionalCustomNames: [
+                    { name: 'myCustomDescribe', type: 'suite', interfaces: [ 'BDD' ] }
+                ]
+            });
 
             expect(suiteNames).to.deep.equal([
                 'describe',
@@ -249,9 +298,31 @@ describe('mocha names', () => {
         });
 
         it('doesn’t return the additional suite names when base names shouldn’t be included', () => {
-            const suiteNames = getSuiteNames({ additionalSuiteNames: [ 'myCustomDescribe' ], baseNames: false });
+            const suiteNames = getSuiteNames({ additionalSuiteNames: [ 'myCustomDescribe' ], modifiersOnly: true });
 
             expect(suiteNames).to.deep.equal([]);
         });
+
+        it('returns the additional skip modifiers', () => {
+            const suiteNames = getSuiteNames({
+                additionalCustomNames: [
+                    { name: 'myCustomDescribe', type: 'suite', interfaces: [ 'BDD' ] },
+                    { name: 'myCustomSuite', type: 'suite', interfaces: [ 'TDD' ] }
+                ],
+                modifiers: [ 'skip' ],
+                modifiersOnly: true
+            });
+
+            expect(suiteNames).to.deep.equal([
+                'describe.skip',
+                'context.skip',
+                'suite.skip',
+                'myCustomDescribe.skip',
+                'myCustomSuite.skip',
+                'xdescribe',
+                'xcontext',
+                'xmyCustomDescribe'
+            ]);
+        });
     });
 });