Repository: eslint/eslint
Pull request #11299 (2019-01-21 02:49:53)

Chore: increase camelcase test coverage
<!--
    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).
-->

**What is the purpose of this pull request? (put an "X" next to item)**

[ ] Documentation update
[ ] Bug fix ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/bug-report.md))
[ ] New rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-proposal.md))
[ ] Changes an existing rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-change-proposal.md))
[ ] Add autofixing to a rule
[ ] Add a CLI option
[ ] Add something to the core
[X] Other, please explain:

Increase test coverage

<!--
    If the item you've checked above has a template, please paste the template questions below and answer them. (If this pull request is addressing an issue, you can just paste a link to the issue here instead.)
-->

<!--
    Please ensure your pull request is ready:

    - Read the pull request guide (https://eslint.org/docs/developer-guide/contributing/pull-requests)
    - Include tests for this change
    - Update documentation for this change (if appropriate)
-->

<!--
    The following is required for all pull requests:
-->

**What changes did you make? (Give an overview)**

Increase test coverage in lib/rules/camelcase.js by writing test cases that hit missing branches/statements

**Is there anything you'd like reviewers to focus on?**
Coverage added in this screenshot
![image](https://user-images.githubusercontent.com/3923164/51450089-1ca1d580-1ce4-11e9-92e4-223d8f4c2377.png)





Patch:
diff --git a/Makefile.js b/Makefile.js
index f38de4e37..fc1d5b40c 100644
--- a/Makefile.js
+++ b/Makefile.js
@@ -117,27 +117,6 @@ function fileType(extension) {
     };
 }
 
-/**
- * Generates a static file that includes each rule by name rather than dynamically
- * looking up based on directory. This is used for the browser version of ESLint.
- * @param {string} basedir The directory in which to look for code.
- * @returns {void}
- */
-function generateRulesIndex(basedir) {
-    let output = "module.exports = function() {\n";
-
-    output += "    var rules = Object.create(null);\n";
-
-    find(`${basedir}rules/`).filter(fileType("js")).forEach(filename => {
-        const basename = path.basename(filename, ".js");
-
-        output += `    rules["${basename}"] = require("./rules/${basename}");\n`;
-    });
-
-    output += "\n    return rules;\n};";
-    output.to(`${basedir}load-rules.js`);
-}
-
 /**
  * Executes a command and returns the output instead of printing it to stdout.
  * @param {string} cmd The command string to execute.
@@ -841,25 +820,16 @@ target.browserify = function() {
         mkdir(BUILD_DIR);
     }
 
-    // 2. copy files into temp directory
-    cp("-r", "lib/*", TEMP_DIR);
-
-    // 3. delete the load-rules.js file
-    rm("-rf", `${TEMP_DIR}load-rules.js`);
+    // 2. browserify the temp directory
+    exec(`${getBinFile("browserify")} -x espree lib/linter.js -o ${BUILD_DIR}eslint.js -s eslint --global-transform [ babelify --presets [ es2015 ] ]`);
 
-    // 4. create new load-rule.js with hardcoded requires
-    generateRulesIndex(TEMP_DIR);
-
-    // 5. browserify the temp directory
-    exec(`${getBinFile("browserify")} -x espree ${TEMP_DIR}linter.js -o ${BUILD_DIR}eslint.js -s eslint --global-transform [ babelify --presets [ es2015 ] ]`);
-
-    // 6. Browserify espree
+    // 3. Browserify espree
     exec(`${getBinFile("browserify")} -r espree -o ${TEMP_DIR}espree.js --global-transform [ babelify --presets [ es2015 ] ]`);
 
-    // 7. Concatenate Babel polyfill, Espree, and ESLint files together
+    // 4. Concatenate Babel polyfill, Espree, and ESLint files together
     cat("./node_modules/babel-polyfill/dist/polyfill.js", `${TEMP_DIR}espree.js`, `${BUILD_DIR}eslint.js`).to(`${BUILD_DIR}eslint.js`);
 
-    // 8. remove temp directory
+    // 5. remove temp directory
     rm("-rf", TEMP_DIR);
 };
 
@@ -867,7 +837,6 @@ target.checkRuleFiles = function() {
 
     echo("Validating rules");
 
-    const eslintRecommended = require("./conf/eslint-recommended").rules;
     const ruleTypes = require("./tools/rule-types.json");
     const ruleFiles = find("lib/rules/").filter(fileType("js"));
     let errors = 0;
@@ -876,15 +845,6 @@ target.checkRuleFiles = function() {
         const basename = path.basename(filename, ".js");
         const docFilename = `docs/rules/${basename}.md`;
 
-        /**
-         * Check if basename is present in eslint:recommended configuration.
-         * @returns {boolean} true if present
-         * @private
-         */
-        function isInConfig() {
-            return Object.prototype.hasOwnProperty.call(eslintRecommended, basename);
-        }
-
         /**
          * Check if basename is present in rule-types.json file.
          * @returns {boolean} true if present
@@ -926,12 +886,6 @@ target.checkRuleFiles = function() {
             }
         }
 
-        // check for recommended configuration
-        if (!isInConfig()) {
-            console.error("Missing eslint:recommended setting for %s in conf/eslint-recommended.js", basename);
-            errors++;
-        }
-
         // check for recommended configuration
         if (!isInRuleTypes()) {
             console.error("Missing setting for %s in tools/rule-types.json", basename);
diff --git a/conf/eslint-all.js b/conf/eslint-all.js
index 43db54fb7..3850fcea3 100644
--- a/conf/eslint-all.js
+++ b/conf/eslint-all.js
@@ -9,16 +9,14 @@
 // Requirements
 //------------------------------------------------------------------------------
 
-const load = require("../lib/load-rules"),
-    Rules = require("../lib/rules");
-const rules = new Rules();
+const builtInRules = require("../lib/built-in-rules-index");
 
 //------------------------------------------------------------------------------
 // Helpers
 //------------------------------------------------------------------------------
 
-const enabledRules = Object.keys(load()).reduce((result, ruleId) => {
-    if (!rules.get(ruleId).meta.deprecated) {
+const enabledRules = Object.keys(builtInRules).reduce((result, ruleId) => {
+    if (!builtInRules[ruleId].meta.deprecated) {
         result[ruleId] = "error";
     }
     return result;
diff --git a/conf/eslint-recommended.js b/conf/eslint-recommended.js
index ca0ca8fa9..447a99638 100644
--- a/conf/eslint-recommended.js
+++ b/conf/eslint-recommended.js
@@ -6,274 +6,10 @@
 
 "use strict";
 
-/* eslint sort-keys: ["error", "asc"] */
+const builtInRules = require("../lib/built-in-rules-index");
 
 module.exports = {
-    rules: {
-        "accessor-pairs": "off",
-        "array-bracket-newline": "off",
-        "array-bracket-spacing": "off",
-        "array-callback-return": "off",
-        "array-element-newline": "off",
-        "arrow-body-style": "off",
-        "arrow-parens": "off",
-        "arrow-spacing": "off",
-        "block-scoped-var": "off",
-        "block-spacing": "off",
-        "brace-style": "off",
-        "callback-return": "off",
-        camelcase: "off",
-        "capitalized-comments": "off",
-        "class-methods-use-this": "off",
-        "comma-dangle": "off",
-        "comma-spacing": "off",
-        "comma-style": "off",
-        complexity: "off",
-        "computed-property-spacing": "off",
-        "consistent-return": "off",
-        "consistent-this": "off",
-        "constructor-super": "error",
-        curly: "off",
-        "default-case": "off",
-        "dot-location": "off",
-        "dot-notation": "off",
-        "eol-last": "off",
-        eqeqeq: "off",
-        "for-direction": "error",
-        "func-call-spacing": "off",
-        "func-name-matching": "off",
-        "func-names": "off",
-        "func-style": "off",
-        "function-paren-newline": "off",
-        "generator-star-spacing": "off",
-        "getter-return": "error",
-        "global-require": "off",
-        "guard-for-in": "off",
-        "handle-callback-err": "off",
-        "id-blacklist": "off",
-        "id-length": "off",
-        "id-match": "off",
-        "implicit-arrow-linebreak": "off",
-        indent: "off",
-        "indent-legacy": "off",
-        "init-declarations": "off",
-        "jsx-quotes": "off",
-        "key-spacing": "off",
-        "keyword-spacing": "off",
-        "line-comment-position": "off",
-        "linebreak-style": "off",
-        "lines-around-comment": "off",
-        "lines-around-directive": "off",
-        "lines-between-class-members": "off",
-        "max-classes-per-file": "off",
-        "max-depth": "off",
-        "max-len": "off",
-        "max-lines": "off",
-        "max-lines-per-function": "off",
-        "max-nested-callbacks": "off",
-        "max-params": "off",
-        "max-statements": "off",
-        "max-statements-per-line": "off",
-        "multiline-comment-style": "off",
-        "multiline-ternary": "off",
-        "new-cap": "off",
-        "new-parens": "off",
-        "newline-after-var": "off",
-        "newline-before-return": "off",
-        "newline-per-chained-call": "off",
-        "no-alert": "off",
-        "no-array-constructor": "off",
-        "no-async-promise-executor": "off",
-        "no-await-in-loop": "off",
-        "no-bitwise": "off",
-        "no-buffer-constructor": "off",
-        "no-caller": "off",
-        "no-case-declarations": "error",
-        "no-catch-shadow": "off",
-        "no-class-assign": "error",
-        "no-compare-neg-zero": "error",
-        "no-cond-assign": "error",
-        "no-confusing-arrow": "off",
-        "no-console": "error",
-        "no-const-assign": "error",
-        "no-constant-condition": "error",
-        "no-continue": "off",
-        "no-control-regex": "error",
-        "no-debugger": "error",
-        "no-delete-var": "error",
-        "no-div-regex": "off",
-        "no-dupe-args": "error",
-        "no-dupe-class-members": "error",
-        "no-dupe-keys": "error",
-        "no-duplicate-case": "error",
-        "no-duplicate-imports": "off",
-        "no-else-return": "off",
-        "no-empty": "error",
-        "no-empty-character-class": "error",
-        "no-empty-function": "off",
-        "no-empty-pattern": "error",
-        "no-eq-null": "off",
-        "no-eval": "off",
-        "no-ex-assign": "error",
-        "no-extend-native": "off",
-        "no-extra-bind": "off",
-        "no-extra-boolean-cast": "error",
-        "no-extra-label": "off",
-        "no-extra-parens": "off",
-        "no-extra-semi": "error",
-        "no-fallthrough": "error",
-        "no-floating-decimal": "off",
-        "no-func-assign": "error",
-        "no-global-assign": "error",
-        "no-implicit-coercion": "off",
-        "no-implicit-globals": "off",
-        "no-implied-eval": "off",
-        "no-inline-comments": "off",
-        "no-inner-declarations": "error",
-        "no-invalid-regexp": "error",
-        "no-invalid-this": "off",
-        "no-irregular-whitespace": "error",
-        "no-iterator": "off",
-        "no-label-var": "off",
-        "no-labels": "off",
-        "no-lone-blocks": "off",
-        "no-lonely-if": "off",
-        "no-loop-func": "off",
-        "no-magic-numbers": "off",
-        "no-misleading-character-class": "off",
-        "no-mixed-operators": "off",
-        "no-mixed-requires": "off",
-        "no-mixed-spaces-and-tabs": "error",
-        "no-multi-assign": "off",
-        "no-multi-spaces": "off",
-        "no-multi-str": "off",
-        "no-multiple-empty-lines": "off",
-        "no-native-reassign": "off",
-        "no-negated-condition": "off",
-        "no-negated-in-lhs": "off",
-        "no-nested-ternary": "off",
-        "no-new": "off",
-        "no-new-func": "off",
-        "no-new-object": "off",
-        "no-new-require": "off",
-        "no-new-symbol": "error",
-        "no-new-wrappers": "off",
-        "no-obj-calls": "error",
-        "no-octal": "error",
-        "no-octal-escape": "off",
-        "no-param-reassign": "off",
-        "no-path-concat": "off",
-        "no-plusplus": "off",
-        "no-process-env": "off",
-        "no-process-exit": "off",
-        "no-proto": "off",
-        "no-prototype-builtins": "off",
-        "no-redeclare": "error",
-        "no-regex-spaces": "error",
-        "no-restricted-globals": "off",
-        "no-restricted-imports": "off",
-        "no-restricted-modules": "off",
-        "no-restricted-properties": "off",
-        "no-restricted-syntax": "off",
-        "no-return-assign": "off",
-        "no-return-await": "off",
-        "no-script-url": "off",
-        "no-self-assign": "error",
-        "no-self-compare": "off",
-        "no-sequences": "off",
-        "no-shadow": "off",
-        "no-shadow-restricted-names": "off",
-        "no-spaced-func": "off",
-        "no-sparse-arrays": "error",
-        "no-sync": "off",
-        "no-tabs": "off",
-        "no-template-curly-in-string": "off",
-        "no-ternary": "off",
-        "no-this-before-super": "error",
-        "no-throw-literal": "off",
-        "no-trailing-spaces": "off",
-        "no-undef": "error",
-        "no-undef-init": "off",
-        "no-undefined": "off",
-        "no-underscore-dangle": "off",
-        "no-unexpected-multiline": "error",
-        "no-unmodified-loop-condition": "off",
-        "no-unneeded-ternary": "off",
-        "no-unreachable": "error",
-        "no-unsafe-finally": "error",
-        "no-unsafe-negation": "error",
-        "no-unused-expressions": "off",
-        "no-unused-labels": "error",
-        "no-unused-vars": "error",
-        "no-use-before-define": "off",
-        "no-useless-call": "off",
-        "no-useless-catch": "off",
-        "no-useless-computed-key": "off",
-        "no-useless-concat": "off",
-        "no-useless-constructor": "off",
-        "no-useless-escape": "error",
-        "no-useless-rename": "off",
-        "no-useless-return": "off",
-        "no-var": "off",
-        "no-void": "off",
-        "no-warning-comments": "off",
-        "no-whitespace-before-property": "off",
-        "no-with": "off",
-        "nonblock-statement-body-position": "off",
-        "object-curly-newline": "off",
-        "object-curly-spacing": "off",
-        "object-property-newline": "off",
-        "object-shorthand": "off",
-        "one-var": "off",
-        "one-var-declaration-per-line": "off",
-        "operator-assignment": "off",
-        "operator-linebreak": "off",
-        "padded-blocks": "off",
-        "padding-line-between-statements": "off",
-        "prefer-arrow-callback": "off",
-        "prefer-const": "off",
-        "prefer-destructuring": "off",
-        "prefer-numeric-literals": "off",
-        "prefer-object-spread": "off",
-        "prefer-promise-reject-errors": "off",
-        "prefer-reflect": "off",
-        "prefer-rest-params": "off",
-        "prefer-spread": "off",
-        "prefer-template": "off",
-        "quote-props": "off",
-        quotes: "off",
-        radix: "off",
-        "require-atomic-updates": "off",
-        "require-await": "off",
-        "require-jsdoc": "off",
-        "require-unicode-regexp": "off",
-        "require-yield": "error",
-        "rest-spread-spacing": "off",
-        semi: "off",
-        "semi-spacing": "off",
-        "semi-style": "off",
-        "sort-imports": "off",
-        "sort-keys": "off",
-        "sort-vars": "off",
-        "space-before-blocks": "off",
-        "space-before-function-paren": "off",
-        "space-in-parens": "off",
-        "space-infix-ops": "off",
-        "space-unary-ops": "off",
-        "spaced-comment": "off",
-        strict: "off",
-        "switch-colon-spacing": "off",
-        "symbol-description": "off",
-        "template-curly-spacing": "off",
-        "template-tag-spacing": "off",
-        "unicode-bom": "off",
-        "use-isnan": "error",
-        "valid-jsdoc": "off",
-        "valid-typeof": "error",
-        "vars-on-top": "off",
-        "wrap-iife": "off",
-        "wrap-regex": "off",
-        "yield-star-spacing": "off",
-        yoda: "off"
-    }
+    rules: Object.assign({}, ...Object.keys(builtInRules).map(ruleId => ({
+        [ruleId]: builtInRules[ruleId].meta.docs.recommended ? "error" : "off"
+    })))
 };
diff --git a/docs/rules/no-async-promise-executor.md b/docs/rules/no-async-promise-executor.md
index 82fed5369..58655b79e 100644
--- a/docs/rules/no-async-promise-executor.md
+++ b/docs/rules/no-async-promise-executor.md
@@ -26,7 +26,7 @@ This rule aims to disallow async Promise executor functions.
 Examples of **incorrect** code for this rule:
 
 ```js
-const result = new Promise(async (resolve, reject) => {
+const foo = new Promise(async (resolve, reject) => {
   readFile('foo.txt', function(err, result) {
     if (err) {
       reject(err);
@@ -44,7 +44,7 @@ const result = new Promise(async (resolve, reject) => {
 Examples of **correct** code for this rule:
 
 ```js
-const result = new Promise((resolve, reject) => {
+const foo = new Promise((resolve, reject) => {
   readFile('foo.txt', function(err, result) {
     if (err) {
       reject(err);
diff --git a/docs/user-guide/configuring.md b/docs/user-guide/configuring.md
index 13ec086ae..1eed790a2 100644
--- a/docs/user-guide/configuring.md
+++ b/docs/user-guide/configuring.md
@@ -45,7 +45,7 @@ Here's an example `.eslintrc.json` file:
         }
     },
     "rules": {
-        "semi": 2
+        "semi": "error"
     }
 }
 ```
@@ -208,19 +208,19 @@ To specify globals using a comment inside of your JavaScript file, use the follo
 /* global var1, var2 */
 ```
 
-This defines two global variables, `var1` and `var2`. If you want to optionally specify that these global variables should never be written to (only read), then you can set each with a `false` flag:
+This defines two global variables, `var1` and `var2`. If you want to optionally specify that these global variables can be written to (rather than only being read), then you can set each with a `writeable` flag:
 
 ```js
-/* global var1:false, var2:false */
+/* global var1:writeable, var2:writeable */
 ```
 
-To configure global variables inside of a configuration file, use the `globals` key and indicate the global variables you want to use. Set each global variable name equal to `true` to allow the variable to be overwritten or `false` to disallow overwriting. For example:
+To configure global variables inside of a configuration file, set the `globals` configuration property to an object containing keys named for each of the global variables you want to use. For each global variable key, set the corresponding value equal to `"writeable"` to allow the variable to be overwritten or `"readable"` to disallow overwriting. For example:
 
 ```json
 {
     "globals": {
-        "var1": true,
-        "var2": false
+        "var1": "writeable",
+        "var2": "readable"
     }
 }
 ```
@@ -230,12 +230,27 @@ And in YAML:
 ```yaml
 ---
   globals:
-    var1: true
-    var2: false
+    var1: writeable
+    var2: readable
 ```
 
 These examples allow `var1` to be overwritten in your code, but disallow it for `var2`.
 
+Globals can be disabled with the string `"off"`. For example, in an environment where most ES2015 globals are available but `Promise` is unavailable, you might use this config:
+
+```json
+{
+    "env": {
+        "es6": true
+    },
+    "globals": {
+        "Promise": "off"
+    }
+}
+```
+
+For historical reasons, the boolean values `false` and `true` can also be used to configure globals, and are equivalent to `"readable"` and `"writeable"`, respectively. However, this usage of booleans is deprecated.
+
 **Note:** Enable the [no-global-assign](../rules/no-global-assign.md) rule to disallow modifications to read-only global variables in your code.
 
 ## Configuring Plugins
@@ -801,15 +816,15 @@ In your `.eslintrc.json`:
 ```json
 {
   "rules": {
-    "quotes": [ 2, "double" ]
+    "quotes": ["error", "double"]
   },
 
   "overrides": [
     {
-      "files": [ "bin/*.js", "lib/*.js" ],
+      "files": ["bin/*.js", "lib/*.js"],
       "excludedFiles": "*.test.js",
       "rules": {
-        "quotes": [ 2, "single" ]
+        "quotes": ["error", "single"]
       }
     }
   ]
diff --git a/lib/cli-engine.js b/lib/cli-engine.js
index ce9fe289b..d7de2592a 100644
--- a/lib/cli-engine.js
+++ b/lib/cli-engine.js
@@ -29,7 +29,8 @@ const fs = require("fs"),
     hash = require("./util/hash"),
     ModuleResolver = require("./util/module-resolver"),
     naming = require("./util/naming"),
-    pkg = require("../package.json");
+    pkg = require("../package.json"),
+    loadRules = require("./load-rules");
 
 const debug = require("debug")("eslint:cli-engine");
 const resolver = new ModuleResolver();
@@ -447,7 +448,7 @@ class CLIEngine {
 
             this.options.rulePaths.forEach(rulesdir => {
                 debug(`Loading rules from ${rulesdir}`);
-                this.linter.rules.load(rulesdir, cwd);
+                this.linter.defineRules(loadRules(rulesdir, cwd));
             });
         }
 
diff --git a/lib/config.js b/lib/config.js
index abcb38b50..9c8438859 100644
--- a/lib/config.js
+++ b/lib/config.js
@@ -71,7 +71,7 @@ class Config {
         const options = providedOptions || {};
 
         this.linterContext = linterContext;
-        this.plugins = new Plugins(linterContext.environments, linterContext.rules);
+        this.plugins = new Plugins(linterContext.environments, linterContext.defineRule.bind(linterContext));
 
         this.options = options;
         this.ignore = options.ignore;
diff --git a/lib/config/config-ops.js b/lib/config/config-ops.js
index 6c298d364..48f38b490 100644
--- a/lib/config/config-ops.js
+++ b/lib/config/config-ops.js
@@ -370,5 +370,33 @@ module.exports = {
 
         return patternList.some(pattern => minimatch(filePath, pattern, opts)) &&
             !excludedPatternList.some(excludedPattern => minimatch(filePath, excludedPattern, opts));
+    },
+
+    /**
+     * Normalizes a value for a global in a config
+     * @param {(boolean|string|null)} configuredValue The value given for a global in configuration or in
+     * a global directive comment
+     * @returns {("readable"|"writeable"|"off")} The value normalized as a string
+     */
+    normalizeConfigGlobal(configuredValue) {
+        switch (configuredValue) {
+            case "off":
+                return "off";
+
+            case true:
+            case "true":
+            case "writeable":
+                return "writeable";
+
+            case null:
+            case false:
+            case "false":
+            case "readable":
+                return "readable";
+
+            // Fallback to minimize compatibility impact
+            default:
+                return "writeable";
+        }
     }
 };
diff --git a/lib/config/config-rule.js b/lib/config/config-rule.js
index e4221406d..29aac0b9a 100644
--- a/lib/config/config-rule.js
+++ b/lib/config/config-rule.js
@@ -10,7 +10,7 @@
 //------------------------------------------------------------------------------
 
 const Rules = require("../rules"),
-    loadRules = require("../load-rules");
+    builtInRules = require("../built-in-rules-index");
 
 const rules = new Rules();
 
@@ -299,9 +299,7 @@ function generateConfigsFromSchema(schema) {
  * @returns {rulesConfig} Hash of rule names and arrays of possible configurations
  */
 function createCoreRuleConfigs() {
-    const ruleList = loadRules();
-
-    return Object.keys(ruleList).reduce((accumulator, id) => {
+    return Object.keys(builtInRules).reduce((accumulator, id) => {
         const rule = rules.get(id);
         const schema = (typeof rule === "function") ? rule.schema : rule.meta.schema;
 
diff --git a/lib/config/plugins.js b/lib/config/plugins.js
index 37c3be497..f32cc26c3 100644
--- a/lib/config/plugins.js
+++ b/lib/config/plugins.js
@@ -24,12 +24,12 @@ class Plugins {
     /**
      * Creates the plugins context
      * @param {Environments} envContext - env context
-     * @param {Rules} rulesContext - rules context
+     * @param {function(string, Rule): void} defineRule - Callback for when a plugin is defined which introduces rules
      */
-    constructor(envContext, rulesContext) {
+    constructor(envContext, defineRule) {
         this._plugins = Object.create(null);
         this._environments = envContext;
-        this._rules = rulesContext;
+        this._defineRule = defineRule;
     }
 
     /**
@@ -45,7 +45,15 @@ class Plugins {
         // load up environments and rules
         this._plugins[shortName] = plugin;
         this._environments.importPlugin(plugin, shortName);
-        this._rules.importPlugin(plugin, shortName);
+
+        if (plugin.rules) {
+            Object.keys(plugin.rules).forEach(ruleId => {
+                const qualifiedRuleId = `${shortName}/${ruleId}`,
+                    rule = plugin.rules[ruleId];
+
+                this._defineRule(qualifiedRuleId, rule);
+            });
+        }
     }
 
     /**
diff --git a/lib/linter.js b/lib/linter.js
index d6d635f9f..29505e9ac 100644
--- a/lib/linter.js
+++ b/lib/linter.js
@@ -70,30 +70,30 @@ const commentParser = new ConfigCommentParser();
  * @returns {void}
  */
 function addDeclaredGlobals(globalScope, configGlobals, commentDirectives) {
-    Object.keys(configGlobals).forEach(name => {
-        let variable = globalScope.set.get(name);
-
-        if (!variable) {
-            variable = new eslintScope.Variable(name, globalScope);
-            variable.eslintExplicitGlobal = false;
-            globalScope.variables.push(variable);
-            globalScope.set.set(name, variable);
-        }
-        variable.writeable = configGlobals[name];
-    });
-
-    Object.keys(commentDirectives.enabledGlobals).forEach(name => {
-        let variable = globalScope.set.get(name);
+    const mergedGlobalsInfo = Object.assign(
+        {},
+        lodash.mapValues(configGlobals, value => ({ sourceComment: null, value: ConfigOps.normalizeConfigGlobal(value) })),
+        lodash.mapValues(commentDirectives.enabledGlobals, ({ comment, value }) => ({ sourceComment: comment, value: ConfigOps.normalizeConfigGlobal(value) }))
+    );
 
-        if (!variable) {
-            variable = new eslintScope.Variable(name, globalScope);
-            variable.eslintExplicitGlobal = true;
-            variable.eslintExplicitGlobalComment = commentDirectives.enabledGlobals[name].comment;
-            globalScope.variables.push(variable);
-            globalScope.set.set(name, variable);
-        }
-        variable.writeable = commentDirectives.enabledGlobals[name].value;
-    });
+    Object.keys(mergedGlobalsInfo)
+        .filter(name => mergedGlobalsInfo[name].value !== "off")
+        .forEach(name => {
+            let variable = globalScope.set.get(name);
+
+            if (!variable) {
+                variable = new eslintScope.Variable(name, globalScope);
+                if (mergedGlobalsInfo[name].sourceComment === null) {
+                    variable.eslintExplicitGlobal = false;
+                } else {
+                    variable.eslintExplicitGlobal = true;
+                    variable.eslintExplicitGlobalComment = mergedGlobalsInfo[name].sourceComment;
+                }
+                globalScope.variables.push(variable);
+                globalScope.set.set(name, variable);
+            }
+            variable.writeable = (mergedGlobalsInfo[name].value === "writeable");
+        });
 
     // mark all exported variables as such
     Object.keys(commentDirectives.exportedVariables).forEach(name => {
@@ -191,12 +191,12 @@ function getDirectiveComments(filename, ast, ruleMapper) {
         } else if (comment.type === "Block") {
             switch (match[1]) {
                 case "exported":
-                    Object.assign(exportedVariables, commentParser.parseBooleanConfig(directiveValue, comment));
+                    Object.assign(exportedVariables, commentParser.parseStringConfig(directiveValue, comment));
                     break;
 
                 case "globals":
                 case "global":
-                    Object.assign(enabledGlobals, commentParser.parseBooleanConfig(directiveValue, comment));
+                    Object.assign(enabledGlobals, commentParser.parseStringConfig(directiveValue, comment));
                     break;
 
                 case "eslint-disable":
@@ -758,6 +758,7 @@ function runRules(sourceCode, configuredRules, ruleMapper, parserOptions, parser
 
 const lastSourceCodes = new WeakMap();
 const loadedParserMaps = new WeakMap();
+const ruleMaps = new WeakMap();
 
 //------------------------------------------------------------------------------
 // Public Interface
@@ -772,9 +773,8 @@ module.exports = class Linter {
     constructor() {
         lastSourceCodes.set(this, null);
         loadedParserMaps.set(this, new Map());
+        ruleMaps.set(this, new Rules());
         this.version = pkg.version;
-
-        this.rules = new Rules();
         this.environments = new Environments();
     }
 
@@ -873,7 +873,7 @@ module.exports = class Linter {
 
         const sourceCode = lastSourceCodes.get(this);
         const commentDirectives = options.allowInlineConfig
-            ? getDirectiveComments(options.filename, sourceCode.ast, ruleId => this.rules.get(ruleId))
+            ? getDirectiveComments(options.filename, sourceCode.ast, ruleId => ruleMaps.get(this).get(ruleId))
             : { configuredRules: {}, enabledGlobals: {}, exportedVariables: {}, problems: [], disableDirectives: [] };
 
         // augment global scope with declared global variables
@@ -891,7 +891,7 @@ module.exports = class Linter {
             lintingProblems = runRules(
                 sourceCode,
                 configuredRules,
-                ruleId => this.rules.get(ruleId),
+                ruleId => ruleMaps.get(this).get(ruleId),
                 parserOptions,
                 parserName,
                 settings,
@@ -957,7 +957,7 @@ module.exports = class Linter {
      * @returns {void}
      */
     defineRule(ruleId, ruleModule) {
-        this.rules.define(ruleId, ruleModule);
+        ruleMaps.get(this).define(ruleId, ruleModule);
     }
 
     /**
@@ -976,7 +976,7 @@ module.exports = class Linter {
      * @returns {Map} All loaded rules
      */
     getRules() {
-        return this.rules.getAllLoadedRules();
+        return ruleMaps.get(this).getAllLoadedRules();
     }
 
     /**
diff --git a/lib/load-rules.js b/lib/load-rules.js
index a9da956bd..a73836246 100644
--- a/lib/load-rules.js
+++ b/lib/load-rules.js
@@ -20,15 +20,12 @@ const rulesDirCache = {};
 
 /**
  * Load all rule modules from specified directory.
- * @param {string} [relativeRulesDir] Path to rules directory, may be relative. Defaults to `lib/rules`.
+ * @param {string} relativeRulesDir Path to rules directory, may be relative.
  * @param {string} cwd Current working directory
  * @returns {Object} Loaded rule modules by rule ids (file names).
  */
 module.exports = function(relativeRulesDir, cwd) {
-
-    const rulesDir = relativeRulesDir
-        ? path.resolve(cwd, relativeRulesDir)
-        : path.join(__dirname, "rules");
+    const rulesDir = path.resolve(cwd, relativeRulesDir);
 
     // cache will help performance as IO operation are expensive
     if (rulesDirCache[rulesDir]) {
diff --git a/lib/rules.js b/lib/rules.js
index ee747311e..d0f909514 100644
--- a/lib/rules.js
+++ b/lib/rules.js
@@ -10,7 +10,6 @@
 //------------------------------------------------------------------------------
 
 const lodash = require("lodash");
-const loadRules = require("./load-rules");
 const ruleReplacements = require("../conf/replacements").rules;
 const builtInRules = require("./built-in-rules-index");
 
@@ -60,7 +59,9 @@ function normalizeRule(rule) {
 class Rules {
     constructor() {
         this._rules = Object.create(null);
-        this.defineAll(builtInRules);
+        Object.keys(builtInRules).forEach(ruleId => {
+            this.define(ruleId, builtInRules[ruleId]);
+        });
     }
 
     /**
@@ -73,46 +74,6 @@ class Rules {
         this._rules[ruleId] = normalizeRule(ruleModule);
     }
 
-    /**
-     * Loads and registers all rules from passed rules directory.
-     * @param {string} [rulesDir] Path to rules directory, may be relative. Defaults to `lib/rules`.
-     * @param {string} cwd Current working directory
-     * @returns {void}
-     */
-    load(rulesDir, cwd) {
-        const newRules = loadRules(rulesDir, cwd);
-
-        this.defineAll(newRules);
-    }
-
-    /**
-     * Pulls a Map of new rules to the defined ones of this instance.
-     * @param {Object} newRules Expects to have an object here that maps the rule ID to the rule definition.
-     * @returns {void}
-     */
-    defineAll(newRules) {
-        Object.keys(newRules).forEach(ruleId => {
-            this.define(ruleId, newRules[ruleId]);
-        });
-    }
-
-    /**
-     * Registers all given rules of a plugin.
-     * @param {Object} plugin The plugin object to import.
-     * @param {string} pluginName The name of the plugin without prefix (`eslint-plugin-`).
-     * @returns {void}
-     */
-    importPlugin(plugin, pluginName) {
-        if (plugin.rules) {
-            Object.keys(plugin.rules).forEach(ruleId => {
-                const qualifiedRuleId = `${pluginName}/${ruleId}`,
-                    rule = plugin.rules[ruleId];
-
-                this.define(qualifiedRuleId, rule);
-            });
-        }
-    }
-
     /**
      * Access rule handler by id (file name).
      * @param {string} ruleId Rule id (file name).
diff --git a/lib/rules/no-constant-condition.js b/lib/rules/no-constant-condition.js
index 39c2928ee..fb36207eb 100644
--- a/lib/rules/no-constant-condition.js
+++ b/lib/rules/no-constant-condition.js
@@ -5,6 +5,13 @@
 
 "use strict";
 
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const EQUALITY_OPERATORS = ["===", "!==", "==", "!="];
+const RELATIONAL_OPERATORS = [">", "<", ">=", "<=", "in", "instanceof"];
+
 //------------------------------------------------------------------------------
 // Rule Definition
 //------------------------------------------------------------------------------
@@ -110,7 +117,18 @@ module.exports = {
                     const isRightShortCircuit = (isRightConstant && isLogicalIdentity(node.right, node.operator));
 
                     return (isLeftConstant && isRightConstant) ||
-                        (node.operator === "||" && isRightConstant && node.right.value) || // in the case of an "OR", we need to know if the right constant value is truthy
+                        (
+
+                            // in the case of an "OR", we need to know if the right constant value is truthy
+                            node.operator === "||" &&
+                            isRightConstant &&
+                            node.right.value &&
+                            (
+                                !node.parent ||
+                                node.parent.type !== "BinaryExpression" ||
+                                !(EQUALITY_OPERATORS.includes(node.parent.operator) || RELATIONAL_OPERATORS.includes(node.parent.operator))
+                            )
+                        ) ||
                         isLeftShortCircuit ||
                         isRightShortCircuit;
                 }
diff --git a/lib/rules/object-shorthand.js b/lib/rules/object-shorthand.js
index ff6a51a4d..31010f0e7 100644
--- a/lib/rules/object-shorthand.js
+++ b/lib/rules/object-shorthand.js
@@ -255,7 +255,7 @@ module.exports = {
                     keyPrefix + keyText + sourceCode.text.slice(tokenBeforeParams.range[1], node.value.range[1])
                 );
             }
-            const arrowToken = sourceCode.getTokens(node.value).find(token => token.value === "=>");
+            const arrowToken = sourceCode.getTokenBefore(node.value.body, { filter: token => token.value === "=>" });
             const tokenBeforeArrow = sourceCode.getTokenBefore(arrowToken);
             const hasParensAroundParameters = tokenBeforeArrow.type === "Punctuator" && tokenBeforeArrow.value === ")";
             const oldParamText = sourceCode.text.slice(sourceCode.getFirstToken(node.value, node.value.async ? 1 : 0).range[0], tokenBeforeArrow.range[1]);
diff --git a/lib/util/config-comment-parser.js b/lib/util/config-comment-parser.js
index 88504caf1..8ee0230cb 100644
--- a/lib/util/config-comment-parser.js
+++ b/lib/util/config-comment-parser.js
@@ -26,14 +26,14 @@ const debug = require("debug")("eslint:config-comment-parser");
 module.exports = class ConfigCommentParser {
 
     /**
-     * Parses a list of "name:boolean_value" or/and "name" options divided by comma or
+     * Parses a list of "name:string_value" or/and "name" options divided by comma or
      * whitespace. Used for "global" and "exported" comments.
      * @param {string} string The string to parse.
      * @param {Comment} comment The comment node which has the string.
-     * @returns {Object} Result map object of names and boolean values
+     * @returns {Object} Result map object of names and string values, or null values if no value was provided
      */
-    parseBooleanConfig(string, comment) {
-        debug("Parsing Boolean config");
+    parseStringConfig(string, comment) {
+        debug("Parsing String config");
 
         const items = {};
 
@@ -45,13 +45,10 @@ module.exports = class ConfigCommentParser {
                 return;
             }
 
-            // value defaults to "false" (if not provided), e.g: "foo" => ["foo", "false"]
-            const [key, value = "false"] = name.split(":");
+            // value defaults to null (if not provided), e.g: "foo" => ["foo", null]
+            const [key, value = null] = name.split(":");
 
-            items[key] = {
-                value: value === "true",
-                comment
-            };
+            items[key] = { value, comment };
         });
         return items;
     }


Test patch:
diff --git a/tests/lib/config/config-ops.js b/tests/lib/config/config-ops.js
index f570a1c6f..7cde4ff0f 100644
--- a/tests/lib/config/config-ops.js
+++ b/tests/lib/config/config-ops.js
@@ -898,4 +898,23 @@ describe("ConfigOps", () => {
         error("foo.js", ["/foo.js"], "Invalid override pattern (expected relative path not containing '..'): /foo.js");
         error("foo.js", ["../**"], "Invalid override pattern (expected relative path not containing '..'): ../**");
     });
+
+    describe("normalizeConfigGlobal", () => {
+        [
+            ["off", "off"],
+            [true, "writeable"],
+            ["true", "writeable"],
+            [false, "readable"],
+            ["false", "readable"],
+            [null, "readable"],
+            ["writeable", "writeable"],
+            ["readable", "readable"],
+            ["writable", "writeable"],
+            ["something else", "writeable"]
+        ].forEach(([input, output]) => {
+            it(util.inspect(input), () => {
+                assert.strictEqual(ConfigOps.normalizeConfigGlobal(input), output);
+            });
+        });
+    });
 });
diff --git a/tests/lib/config/config-rule.js b/tests/lib/config/config-rule.js
index 2a88eb915..810f8aacc 100644
--- a/tests/lib/config/config-rule.js
+++ b/tests/lib/config/config-rule.js
@@ -11,7 +11,7 @@
 
 const assert = require("chai").assert,
     ConfigRule = require("../../../lib/config/config-rule"),
-    loadRules = require("../../../lib/load-rules"),
+    builtInRules = require("../../../lib/built-in-rules-index"),
     schema = require("../../fixtures/config-rule/schemas");
 
 //------------------------------------------------------------------------------
@@ -294,7 +294,7 @@ describe("ConfigRule", () => {
         const rulesConfig = ConfigRule.createCoreRuleConfigs();
 
         it("should create a rulesConfig containing all core rules", () => {
-            const coreRules = loadRules(),
+            const coreRules = builtInRules,
                 expectedRules = Object.keys(coreRules),
                 actualRules = Object.keys(rulesConfig);
 
diff --git a/tests/lib/config/config-validator.js b/tests/lib/config/config-validator.js
index b4cb2e41d..721533c10 100644
--- a/tests/lib/config/config-validator.js
+++ b/tests/lib/config/config-validator.js
@@ -11,7 +11,8 @@
 
 const assert = require("chai").assert,
     Linter = require("../../../lib/linter"),
-    validator = require("../../../lib/config/config-validator");
+    validator = require("../../../lib/config/config-validator"),
+    Rules = require("../../../lib/rules");
 const linter = new Linter();
 
 //------------------------------------------------------------------------------
@@ -96,7 +97,7 @@ describe("Validator", () => {
      * @returns {{create: Function}} The loaded rule
      */
     function ruleMapper(ruleId) {
-        return linter.getRules().get(ruleId);
+        return linter.getRules().get(ruleId) || new Rules().get(ruleId);
     }
 
     beforeEach(() => {
@@ -403,12 +404,12 @@ describe("Validator", () => {
     describe("getRuleOptionsSchema", () => {
 
         it("should return null for a missing rule", () => {
-            assert.strictEqual(validator.getRuleOptionsSchema(linter.rules.get("non-existent-rule")), null);
+            assert.strictEqual(validator.getRuleOptionsSchema(ruleMapper("non-existent-rule")), null);
         });
 
         it("should not modify object schema", () => {
             linter.defineRule("mock-object-rule", mockObjectRule);
-            assert.deepStrictEqual(validator.getRuleOptionsSchema(linter.rules.get("mock-object-rule")), {
+            assert.deepStrictEqual(validator.getRuleOptionsSchema(ruleMapper("mock-object-rule")), {
                 enum: ["first", "second"]
             });
         });
@@ -418,43 +419,43 @@ describe("Validator", () => {
     describe("validateRuleOptions", () => {
 
         it("should throw for incorrect warning level number", () => {
-            const fn = validator.validateRuleOptions.bind(null, linter.rules.get("mock-rule"), "mock-rule", 3, "tests");
+            const fn = validator.validateRuleOptions.bind(null, ruleMapper("mock-rule"), "mock-rule", 3, "tests");
 
             assert.throws(fn, "tests:\n\tConfiguration for rule \"mock-rule\" is invalid:\n\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed '3').\n");
         });
 
         it("should throw for incorrect warning level string", () => {
-            const fn = validator.validateRuleOptions.bind(null, linter.rules.get("mock-rule"), "mock-rule", "booya", "tests");
+            const fn = validator.validateRuleOptions.bind(null, ruleMapper("mock-rule"), "mock-rule", "booya", "tests");
 
             assert.throws(fn, "tests:\n\tConfiguration for rule \"mock-rule\" is invalid:\n\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed '\"booya\"').\n");
         });
 
         it("should throw for invalid-type warning level", () => {
-            const fn = validator.validateRuleOptions.bind(null, linter.rules.get("mock-rule"), "mock-rule", [["error"]], "tests");
+            const fn = validator.validateRuleOptions.bind(null, ruleMapper("mock-rule"), "mock-rule", [["error"]], "tests");
 
             assert.throws(fn, "tests:\n\tConfiguration for rule \"mock-rule\" is invalid:\n\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed '[ \"error\" ]').\n");
         });
 
         it("should only check warning level for nonexistent rules", () => {
-            const fn = validator.validateRuleOptions.bind(null, linter.rules.get("non-existent-rule"), "non-existent-rule", [3, "foobar"], "tests");
+            const fn = validator.validateRuleOptions.bind(null, ruleMapper("non-existent-rule"), "non-existent-rule", [3, "foobar"], "tests");
 
             assert.throws(fn, "tests:\n\tConfiguration for rule \"non-existent-rule\" is invalid:\n\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed '3').\n");
         });
 
         it("should only check warning level for plugin rules", () => {
-            const fn = validator.validateRuleOptions.bind(null, linter.rules.get("plugin/rule"), "plugin/rule", 3, "tests");
+            const fn = validator.validateRuleOptions.bind(null, ruleMapper("plugin/rule"), "plugin/rule", 3, "tests");
 
             assert.throws(fn, "tests:\n\tConfiguration for rule \"plugin/rule\" is invalid:\n\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed '3').\n");
         });
 
         it("should throw for incorrect configuration values", () => {
-            const fn = validator.validateRuleOptions.bind(null, linter.rules.get("mock-rule"), "mock-rule", [2, "frist"], "tests");
+            const fn = validator.validateRuleOptions.bind(null, ruleMapper("mock-rule"), "mock-rule", [2, "frist"], "tests");
 
             assert.throws(fn, "tests:\n\tConfiguration for rule \"mock-rule\" is invalid:\n\tValue \"frist\" should be equal to one of the allowed values.\n");
         });
 
         it("should throw for too many configuration values", () => {
-            const fn = validator.validateRuleOptions.bind(null, linter.rules.get("mock-rule"), "mock-rule", [2, "first", "second"], "tests");
+            const fn = validator.validateRuleOptions.bind(null, ruleMapper("mock-rule"), "mock-rule", [2, "first", "second"], "tests");
 
             assert.throws(fn, "tests:\n\tConfiguration for rule \"mock-rule\" is invalid:\n\tValue [\"first\",\"second\"] should NOT have more than 1 items.\n");
         });
diff --git a/tests/lib/config/plugins.js b/tests/lib/config/plugins.js
index 37b96c048..afde8af44 100644
--- a/tests/lib/config/plugins.js
+++ b/tests/lib/config/plugins.js
@@ -10,7 +10,6 @@
 
 const assert = require("chai").assert,
     Plugins = require("../../../lib/config/plugins"),
-    Rules = require("../../../lib/rules"),
     Environments = require("../../../lib/config/environments");
 
 const proxyquire = require("proxyquire").noCallThru().noPreserveCache();
@@ -24,7 +23,7 @@ describe("Plugins", () => {
     describe("get", () => {
 
         it("should return null when plugin doesn't exist", () => {
-            assert.isNull((new Plugins(new Environments(), new Rules())).get("foo"));
+            assert.isNull((new Plugins(new Environments(), () => {})).get("foo"));
         });
     });
 
@@ -39,8 +38,8 @@ describe("Plugins", () => {
         beforeEach(() => {
             plugin = {};
             scopedPlugin = {};
-            rules = new Rules();
             environments = new Environments();
+            rules = new Map();
             StubbedPlugins = new (proxyquire("../../../lib/config/plugins", {
                 "eslint-plugin-example": plugin,
                 "@scope/eslint-plugin-example": scopedPlugin,
@@ -49,7 +48,7 @@ describe("Plugins", () => {
                         throw new Error("error thrown while loading this module");
                     }
                 }
-            }))(environments, rules);
+            }))(environments, rules.set.bind(rules));
         });
 
         it("should load a plugin when referenced by short name", () => {
@@ -180,7 +179,7 @@ describe("Plugins", () => {
                 };
                 StubbedPlugins.load("@scope/eslint-plugin-example");
 
-                assert.isFalse(rules.getAllLoadedRules().has("example/foo"));
+                assert.isFalse(rules.has("example/foo"));
             });
         });
     });
@@ -189,17 +188,18 @@ describe("Plugins", () => {
 
         let StubbedPlugins,
             plugin1,
-            plugin2;
-        const rules = new Rules(),
-            environments = new Environments();
+            plugin2,
+            rules;
+        const environments = new Environments();
 
         beforeEach(() => {
             plugin1 = {};
             plugin2 = {};
+            rules = new Map();
             StubbedPlugins = new (proxyquire("../../../lib/config/plugins", {
                 "eslint-plugin-example1": plugin1,
                 "eslint-plugin-example2": plugin2
-            }))(environments, rules);
+            }))(environments, rules.set.bind(rules));
         });
 
         it("should load plugins when passed multiple plugins", () => {
diff --git a/tests/lib/linter.js b/tests/lib/linter.js
index ebe59625b..564bcee6b 100644
--- a/tests/lib/linter.js
+++ b/tests/lib/linter.js
@@ -1124,10 +1124,15 @@ describe("Linter", () => {
     });
 
     describe("when evaluating code containing /*global */ and /*globals */ blocks", () => {
-        const code = "/*global a b:true c:false*/ function foo() {} /*globals d:true*/";
 
         it("variables should be available in global scope", () => {
-            const config = { rules: { checker: "error" } };
+            const config = { rules: { checker: "error" }, globals: { Array: "off", ConfigGlobal: "writeable" } };
+            const code = `
+                /*global a b:true c:false d:readable e:writeable Math:off */
+                function foo() {}
+                /*globals f:true*/
+                /* global ConfigGlobal : readable */
+            `;
             let spy;
 
             linter.defineRule("checker", context => {
@@ -1136,7 +1141,12 @@ describe("Linter", () => {
                     const a = getVariable(scope, "a"),
                         b = getVariable(scope, "b"),
                         c = getVariable(scope, "c"),
-                        d = getVariable(scope, "d");
+                        d = getVariable(scope, "d"),
+                        e = getVariable(scope, "e"),
+                        f = getVariable(scope, "f"),
+                        mathGlobal = getVariable(scope, "Math"),
+                        arrayGlobal = getVariable(scope, "Array"),
+                        configGlobal = getVariable(scope, "ConfigGlobal");
 
                     assert.strictEqual(a.name, "a");
                     assert.strictEqual(a.writeable, false);
@@ -1145,7 +1155,15 @@ describe("Linter", () => {
                     assert.strictEqual(c.name, "c");
                     assert.strictEqual(c.writeable, false);
                     assert.strictEqual(d.name, "d");
-                    assert.strictEqual(d.writeable, true);
+                    assert.strictEqual(d.writeable, false);
+                    assert.strictEqual(e.name, "e");
+                    assert.strictEqual(e.writeable, true);
+                    assert.strictEqual(f.name, "f");
+                    assert.strictEqual(f.writeable, true);
+                    assert.strictEqual(mathGlobal, null);
+                    assert.strictEqual(arrayGlobal, null);
+                    assert.strictEqual(configGlobal.name, "ConfigGlobal");
+                    assert.strictEqual(configGlobal.writeable, false);
                 });
 
                 return { Program: spy };
@@ -1454,6 +1472,26 @@ describe("Linter", () => {
             linter.verify(code, config);
             assert(spy && spy.calledOnce);
         });
+
+        it("ES6 global variables can be disabled when the es6 environment is enabled", () => {
+            const config = { rules: { checker: "error" }, globals: { Promise: "off", Symbol: "off", WeakMap: "off" }, env: { es6: true } };
+            let spy;
+
+            linter.defineRule("checker", context => {
+                spy = sandbox.spy(() => {
+                    const scope = context.getScope();
+
+                    assert.strictEqual(getVariable(scope, "Promise"), null);
+                    assert.strictEqual(getVariable(scope, "Symbol"), null);
+                    assert.strictEqual(getVariable(scope, "WeakMap"), null);
+                });
+
+                return { Program: spy };
+            });
+
+            linter.verify(code, config);
+            assert(spy && spy.calledOnce);
+        });
     });
 
     describe("at any time", () => {
diff --git a/tests/lib/load-rules.js b/tests/lib/load-rules.js
new file mode 100644
index 000000000..9d01e6b02
--- /dev/null
+++ b/tests/lib/load-rules.js
@@ -0,0 +1,25 @@
+/**
+ * @fileoverview Tests for loading rules from a directory
+ * @author Teddy Katz
+ */
+
+"use strict";
+
+const assert = require("chai").assert;
+const loadRules = require("../../lib/load-rules");
+
+describe("when given an invalid rules directory", () => {
+    it("should throw an error", () => {
+        assert.throws(() => {
+            loadRules("invalidDir");
+        });
+    });
+});
+
+describe("when given a valid rules directory", () => {
+    it("should load rules and not throw an error", () => {
+        const rules = loadRules("tests/fixtures/rules", process.cwd());
+
+        assert.strictEqual(rules["fixture-rule"], require.resolve("../../tests/fixtures/rules/fixture-rule"));
+    });
+});
diff --git a/tests/lib/rules.js b/tests/lib/rules.js
index 471600f65..25abfd582 100644
--- a/tests/lib/rules.js
+++ b/tests/lib/rules.js
@@ -24,25 +24,6 @@ describe("rules", () => {
         rules = new Rules();
     });
 
-    describe("when given an invalid rules directory", () => {
-        const code = "invaliddir";
-
-        it("should throw an error", () => {
-            assert.throws(() => {
-                rules.load(code);
-            });
-        });
-    });
-
-    describe("when given a valid rules directory", () => {
-        const code = "tests/fixtures/rules";
-
-        it("should load rules and not throw an error", () => {
-            rules.load(code, process.cwd());
-            assert.strictEqual(typeof rules.get("fixture-rule"), "object");
-        });
-    });
-
     describe("when a rule has been defined", () => {
         it("should be able to retrieve the rule", () => {
             const ruleId = "michaelficarra";
@@ -108,30 +89,6 @@ describe("rules", () => {
         });
     });
 
-    describe("when importing plugin rules", () => {
-        const customPlugin = {
-                rules: {
-                    "custom-rule"() { }
-                }
-            },
-            pluginName = "custom-plugin";
-
-        it("should define all plugin rules with a qualified rule id", () => {
-            rules.importPlugin(customPlugin, pluginName);
-
-            assert.isDefined(rules.get("custom-plugin/custom-rule"));
-            assert.strictEqual(rules.get("custom-plugin/custom-rule").create, customPlugin.rules["custom-rule"]);
-        });
-
-        it("should return custom rules as part of getAllLoadedRules", () => {
-            rules.importPlugin(customPlugin, pluginName);
-
-            const allRules = rules.getAllLoadedRules();
-
-            assert.strictEqual(allRules.get("custom-plugin/custom-rule").create, customPlugin.rules["custom-rule"]);
-        });
-    });
-
     describe("when loading all rules", () => {
         it("should return a map", () => {
             const allRules = rules.getAllLoadedRules();
diff --git a/tests/lib/rules/camelcase.js b/tests/lib/rules/camelcase.js
index eb80b4ef4..c8a3914e6 100644
--- a/tests/lib/rules/camelcase.js
+++ b/tests/lib/rules/camelcase.js
@@ -202,6 +202,11 @@ ruleTester.run("camelcase", rule, {
         {
             code: "__option_foo__ = 0;",
             options: [{ allow: ["__option_foo__"] }]
+        },
+        {
+            code: "foo = { [computedBar]: 0 };",
+            options: [{ ignoreDestructuring: true }],
+            parserOptions: { ecmaVersion: 6 }
         }
     ],
     invalid: [
@@ -295,6 +300,17 @@ ruleTester.run("camelcase", rule, {
                 }
             ]
         },
+        {
+            code: "var foo = { bar_baz: boom.bam_pow }",
+            options: [{ ignoreDestructuring: true }],
+            errors: [
+                {
+                    messageId: "notCamelCase",
+                    data: { name: "bar_baz" },
+                    type: "Identifier"
+                }
+            ]
+        },
         {
             code: "foo.qux.boom_pow = { bar: boom.bam_pow }",
             errors: [
@@ -600,6 +616,16 @@ ruleTester.run("camelcase", rule, {
                 }
             ]
         },
+        {
+            code: "const { no_camelcased = foo_bar } = bar;",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                {
+                    message: "Identifier 'no_camelcased' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
         {
             code: "not_ignored_foo = 0;",
             options: [{ allow: ["ignored_bar"] }],
@@ -619,6 +645,17 @@ ruleTester.run("camelcase", rule, {
                     type: "Identifier"
                 }
             ]
+        },
+        {
+            code: "foo = { [computed_bar]: 0 };",
+            options: [{ ignoreDestructuring: true }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                {
+                    message: "Identifier 'computed_bar' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
         }
     ]
 });
diff --git a/tests/lib/rules/no-constant-condition.js b/tests/lib/rules/no-constant-condition.js
index c89f9080d..e27e3b390 100644
--- a/tests/lib/rules/no-constant-condition.js
+++ b/tests/lib/rules/no-constant-condition.js
@@ -61,6 +61,18 @@ ruleTester.run("no-constant-condition", rule, {
         "if(a && 'str'){}",
         "if('str' || abc==='str'){}",
 
+        // #11306
+        "if ((foo || 'bar') === 'baz') {}",
+        "if ((foo || 'bar') !== 'baz') {}",
+        "if ((foo || 'bar') == 'baz') {}",
+        "if ((foo || 'bar') != 'baz') {}",
+        "if ((foo || 233) > 666) {}",
+        "if ((foo || 233) < 666) {}",
+        "if ((foo || 233) >= 666) {}",
+        "if ((foo || 233) <= 666) {}",
+        "if ((key || 'k') in obj) {}",
+        "if ((foo || {}) instanceof obj) {}",
+
         // { checkLoops: false }
         { code: "while(true);", options: [{ checkLoops: false }] },
         { code: "for(;true;);", options: [{ checkLoops: false }] },
diff --git a/tests/lib/rules/object-shorthand.js b/tests/lib/rules/object-shorthand.js
index c52f43af0..19d235290 100644
--- a/tests/lib/rules/object-shorthand.js
+++ b/tests/lib/rules/object-shorthand.js
@@ -931,6 +931,14 @@ ruleTester.run("object-shorthand", rule, {
             parserOptions: { ecmaVersion: 8 },
             errors: [METHOD_ERROR]
         },
+        {
+
+            // https://github.com/eslint/eslint/issues/11305
+            code: "({ key: (arg = () => {}) => {} })",
+            output: "({ key(arg = () => {}) {} })",
+            options: ["always", { avoidExplicitReturnArrows: true }],
+            errors: [METHOD_ERROR]
+        },
         {
             code: `
                 function foo() {
diff --git a/tests/lib/util/config-comment-parser.js b/tests/lib/util/config-comment-parser.js
index 6dbe9381a..41583d65c 100644
--- a/tests/lib/util/config-comment-parser.js
+++ b/tests/lib/util/config-comment-parser.js
@@ -117,77 +117,77 @@ describe("ConfigCommentParser", () => {
 
     });
 
-    describe("parseBooleanConfig", () => {
+    describe("parseStringConfig", () => {
 
         const comment = {};
 
-        it("should parse Boolean config with one item", () => {
+        it("should parse String config with one item", () => {
             const code = "a: true";
-            const result = commentParser.parseBooleanConfig(code, comment);
+            const result = commentParser.parseStringConfig(code, comment);
 
             assert.deepStrictEqual(result, {
                 a: {
-                    value: true,
+                    value: "true",
                     comment
                 }
             });
         });
 
-        it("should parse Boolean config with one item and no value", () => {
+        it("should parse String config with one item and no value", () => {
             const code = "a";
-            const result = commentParser.parseBooleanConfig(code, comment);
+            const result = commentParser.parseStringConfig(code, comment);
 
             assert.deepStrictEqual(result, {
                 a: {
-                    value: false,
+                    value: null,
                     comment
                 }
             });
         });
 
-        it("should parse Boolean config with two items", () => {
-            const code = "a: true b:false";
-            const result = commentParser.parseBooleanConfig(code, comment);
+        it("should parse String config with two items", () => {
+            const code = "a: five b:three";
+            const result = commentParser.parseStringConfig(code, comment);
 
             assert.deepStrictEqual(result, {
                 a: {
-                    value: true,
+                    value: "five",
                     comment
                 },
                 b: {
-                    value: false,
+                    value: "three",
                     comment
                 }
             });
         });
 
-        it("should parse Boolean config with two comma-separated items", () => {
-            const code = "a: true, b:false";
-            const result = commentParser.parseBooleanConfig(code, comment);
+        it("should parse String config with two comma-separated items", () => {
+            const code = "a: seventy, b:ELEVENTEEN";
+            const result = commentParser.parseStringConfig(code, comment);
 
             assert.deepStrictEqual(result, {
                 a: {
-                    value: true,
+                    value: "seventy",
                     comment
                 },
                 b: {
-                    value: false,
+                    value: "ELEVENTEEN",
                     comment
                 }
             });
         });
 
-        it("should parse Boolean config with two comma-separated items and no values", () => {
+        it("should parse String config with two comma-separated items and no values", () => {
             const code = "a , b";
-            const result = commentParser.parseBooleanConfig(code, comment);
+            const result = commentParser.parseStringConfig(code, comment);
 
             assert.deepStrictEqual(result, {
                 a: {
-                    value: false,
+                    value: null,
                     comment
                 },
                 b: {
-                    value: false,
+                    value: null,
                     comment
                 }
             });