Repository: eslint/eslint
Pull request #10407 (2018-05-28 14:39:29)

Docs: remove test coverage badge
Temporaryly removed the badge, since it has being disabled for a long time. 

sorry for not following the template. :(


Patch:
diff --git a/.travis.yml b/.travis.yml
index 5763f04af..ff3e956a7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,6 +3,7 @@ node_js:
     - "6"
     - "8"
     - "9"
+    - "10"
 sudo: false
 branches:
     only:
diff --git a/README.md b/README.md
index cb8c0277d..95c272094 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,6 @@
 [![NPM version][npm-image]][npm-url]
 [![build status][travis-image]][travis-url]
 [![Build status][appveyor-image]][appveyor-url]
-[![Test coverage][coveralls-image]][coveralls-url]
 [![Downloads][downloads-image]][downloads-url]
 [![Bountysource](https://www.bountysource.com/badge/tracker?tracker_id=282608)](https://www.bountysource.com/trackers/282608-eslint?utm_source=282608&utm_medium=shield&utm_campaign=TRACKER_BADGE)
 [![Join the chat at https://gitter.im/eslint/eslint](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/eslint/eslint?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
diff --git a/lib/config/config-validator.js b/lib/config/config-validator.js
index ed9a34cb0..185d4650d 100644
--- a/lib/config/config-validator.js
+++ b/lib/config/config-validator.js
@@ -254,6 +254,11 @@ function validate(config, source, ruleMapper, envContext) {
     validateConfigSchema(config, source);
     validateRules(config.rules, source, ruleMapper);
     validateEnvironment(config.env, source, envContext);
+
+    for (const override of config.overrides || []) {
+        validateRules(override.rules, source, ruleMapper);
+        validateEnvironment(override.env, source, envContext);
+    }
 }
 
 //------------------------------------------------------------------------------
diff --git a/lib/rules/no-extra-parens.js b/lib/rules/no-extra-parens.js
index 9765dfa77..3d4ee19e8 100644
--- a/lib/rules/no-extra-parens.js
+++ b/lib/rules/no-extra-parens.js
@@ -86,7 +86,7 @@ module.exports = {
          * @private
          */
         function ruleApplies(node) {
-            if (node.type === "JSXElement") {
+            if (node.type === "JSXElement" || node.type === "JSXFragment") {
                 const isSingleLine = node.loc.start.line === node.loc.end.line;
 
                 switch (IGNORE_JSX) {
diff --git a/lib/rules/no-useless-escape.js b/lib/rules/no-useless-escape.js
index efc9706f4..2ddab7854 100644
--- a/lib/rules/no-useless-escape.js
+++ b/lib/rules/no-useless-escape.js
@@ -174,7 +174,7 @@ module.exports = {
                  * JSXAttribute doesn't have any escape sequence: https://facebook.github.io/jsx/.
                  * In addition, backticks are not supported by JSX yet: https://github.com/facebook/jsx/issues/25.
                  */
-                if (node.parent.type === "JSXAttribute" || node.parent.type === "JSXElement") {
+                if (node.parent.type === "JSXAttribute" || node.parent.type === "JSXElement" || node.parent.type === "JSXFragment") {
                     return;
                 }
 
diff --git a/lib/rules/prefer-const.js b/lib/rules/prefer-const.js
index a24a00284..a7c46b34d 100644
--- a/lib/rules/prefer-const.js
+++ b/lib/rules/prefer-const.js
@@ -5,6 +5,8 @@
 
 "use strict";
 
+const astUtils = require("../ast-utils");
+
 //------------------------------------------------------------------------------
 // Helpers
 //------------------------------------------------------------------------------
@@ -55,6 +57,54 @@ function canBecomeVariableDeclaration(identifier) {
     );
 }
 
+/**
+ * Checks if an property or element is from outer scope or function parameters
+ * in destructing pattern.
+ *
+ * @param {string} name - A variable name to be checked.
+ * @param {eslint-scope.Scope} initScope - A scope to start find.
+ * @returns {boolean} Indicates if the variable is from outer scope or function parameters.
+ */
+function isOuterVariableInDestructing(name, initScope) {
+    if (initScope.through.find(ref => ref.resolved && ref.resolved.name === name)) {
+        return true;
+    }
+
+    const variable = astUtils.getVariableByName(initScope, name);
+
+    if (variable !== null) {
+        return variable.defs.some(def => def.type === "Parameter");
+    }
+
+    return false;
+}
+
+/**
+ * Gets the VariableDeclarator/AssignmentExpression node that a given reference
+ * belongs to.
+ * This is used to detect a mix of reassigned and never reassigned in a
+ * destructuring.
+ *
+ * @param {eslint-scope.Reference} reference - A reference to get.
+ * @returns {ASTNode|null} A VariableDeclarator/AssignmentExpression node or
+ *      null.
+ */
+function getDestructuringHost(reference) {
+    if (!reference.isWrite()) {
+        return null;
+    }
+    let node = reference.identifier.parent;
+
+    while (PATTERN_TYPE.test(node.type)) {
+        node = node.parent;
+    }
+
+    if (!DESTRUCTURING_HOST_TYPE.test(node.type)) {
+        return null;
+    }
+    return node;
+}
+
 /**
  * Gets an identifier node of a given variable.
  *
@@ -102,6 +152,32 @@ function getIdentifierIfShouldBeConst(variable, ignoreReadBeforeAssign) {
             if (isReassigned) {
                 return null;
             }
+
+            const destructuringHost = getDestructuringHost(reference);
+
+            if (destructuringHost !== null && destructuringHost.left !== void 0) {
+                const leftNode = destructuringHost.left;
+                let hasOuterVariables = false;
+
+                if (leftNode.type === "ObjectPattern") {
+                    const properties = leftNode.properties;
+
+                    hasOuterVariables = properties
+                        .filter(prop => prop.value)
+                        .map(prop => prop.value.name)
+                        .some(name => isOuterVariableInDestructing(name, variable.scope));
+                } else if (leftNode.type === "ArrayPattern") {
+                    const elements = leftNode.elements;
+
+                    hasOuterVariables = elements
+                        .map(element => element.name)
+                        .some(name => isOuterVariableInDestructing(name, variable.scope));
+                }
+                if (hasOuterVariables) {
+                    return null;
+                }
+            }
+
             writer = reference;
 
         } else if (reference.isRead() && writer === null) {
@@ -131,32 +207,6 @@ function getIdentifierIfShouldBeConst(variable, ignoreReadBeforeAssign) {
     return writer.identifier;
 }
 
-/**
- * Gets the VariableDeclarator/AssignmentExpression node that a given reference
- * belongs to.
- * This is used to detect a mix of reassigned and never reassigned in a
- * destructuring.
- *
- * @param {eslint-scope.Reference} reference - A reference to get.
- * @returns {ASTNode|null} A VariableDeclarator/AssignmentExpression node or
- *      null.
- */
-function getDestructuringHost(reference) {
-    if (!reference.isWrite()) {
-        return null;
-    }
-    let node = reference.identifier.parent;
-
-    while (PATTERN_TYPE.test(node.type)) {
-        node = node.parent;
-    }
-
-    if (!DESTRUCTURING_HOST_TYPE.test(node.type)) {
-        return null;
-    }
-    return node;
-}
-
 /**
  * Groups by the VariableDeclarator/AssignmentExpression node that each
  * reference of given variables belongs to.
@@ -290,7 +340,7 @@ module.exports = {
                     (varDeclParent.parent.type === "ForInStatement" || varDeclParent.parent.type === "ForOfStatement" || varDeclParent.declarations[0].init) &&
 
                     /*
-                     * If options.destucturing is "all", then this warning will not occur unless
+                     * If options.destructuring is "all", then this warning will not occur unless
                      * every assignment in the destructuring should be const. In that case, it's safe
                      * to apply the fix.
                      */
diff --git a/lib/rules/prefer-object-spread.js b/lib/rules/prefer-object-spread.js
index aa69824a5..d6577df7d 100644
--- a/lib/rules/prefer-object-spread.js
+++ b/lib/rules/prefer-object-spread.js
@@ -196,6 +196,34 @@ function autofixObjectLiteral(node, sourceCode) {
     };
 }
 
+/**
+ * Check if the node has modified a given variable
+ * @param {ASTNode|null} node - The node that the rule warns on, i.e. the Object.assign call
+ * @returns {boolean} - true if node is an assignment, variable declaration, or import statement
+ */
+function isModifier(node) {
+    if (!node.type) {
+        return false;
+    }
+
+    return node.type === "AssignmentExpression" ||
+        node.type === "VariableDeclarator" ||
+        node.type === "ImportDeclaration";
+}
+
+/**
+ * Check if the node has modified a given variable
+ * @param {array} references - list of reference nodes
+ * @returns {boolean} - true if node is has been overwritten by an assignment or import
+ */
+function modifyingObjectReference(references) {
+    return references.some(ref => (
+        ref.identifier &&
+        ref.identifier.parent &&
+        isModifier(ref.identifier.parent)
+    ));
+}
+
 
 module.exports = {
     meta: {
@@ -216,35 +244,25 @@ module.exports = {
 
     create: function rule(context) {
         const sourceCode = context.getSourceCode();
-        let overWritingNativeObject = false;
-
-        return {
-            VariableDeclaration(node) {
+        const scope = context.getScope();
+        const objectVariable = scope.variables.filter(variable => variable.name === "Object");
+        const moduleReferences = scope.childScopes.filter(childScope => {
+            const varNamedObject = childScope.variables.filter(variable => variable.name === "Object");
 
-                /*
-                 * If a declared variable is overwriting the native `Object`, eg. `const Object = 'something'`
-                 * we want to skip warning on them since the behavior might be different.
-                 */
-                if (node.declarations.length) {
-                    const declarationNames = node.declarations.map(dec => dec.id);
-                    const nativeObjectOverride = declarationNames.filter(identifier => identifier.name === "Object");
+            return childScope.type === "module" && varNamedObject.length;
+        });
+        const references = [].concat(...objectVariable.map(variable => variable.references || []));
 
-                    if (nativeObjectOverride.length) {
-                        overWritingNativeObject = true;
-                    }
-                }
-            },
-            AssignmentExpression(node) {
+        return {
+            CallExpression(node) {
 
                 /*
-                 * If an assignment is reassigning the native `Object`, eg. `Object = 'something'`
-                 * we want to skip warning on them since the behavior might be different.
+                 * If current file is either importing Object or redefining it,
+                 * we skip warning on this rule.
                  */
-                if (node.left.name === "Object") {
-                    overWritingNativeObject = true;
+                if (moduleReferences.length || (references.length && modifyingObjectReference(references))) {
+                    return;
                 }
-            },
-            CallExpression(node) {
 
                 /*
                  * The condition below is cases where Object.assign has a single argument and
@@ -252,7 +270,6 @@ module.exports = {
                  * For now, we will warn on this case and autofix it.
                  */
                 if (
-                    !overWritingNativeObject &&
                     node.arguments.length === 1 &&
                     node.arguments[0].type === "ObjectExpression" &&
                     isObjectAssign(node)
@@ -270,7 +287,6 @@ module.exports = {
                  * that can be spread. e.g `Object.assign({ foo: bar }, baz)`
                  */
                 if (
-                    !overWritingNativeObject &&
                     node.arguments.length > 1 &&
                     node.arguments[0].type === "ObjectExpression" &&
                     isObjectAssign(node) &&
diff --git a/lib/rules/quotes.js b/lib/rules/quotes.js
index fa8bbae5f..78794e72b 100644
--- a/lib/rules/quotes.js
+++ b/lib/rules/quotes.js
@@ -132,6 +132,7 @@ module.exports = {
          *
          * - `<div className="foo"></div>` ... If the literal is an attribute value, the parent of the literal is `JSXAttribute`.
          * - `<div>foo</div>` ... If the literal is a text content, the parent of the literal is `JSXElement`.
+         * - `<>foo</>` ... If the literal is a text content, the parent of the literal is `JSXFragment`.
          *
          * In particular, this function returns `false` in the following cases:
          *
@@ -146,7 +147,7 @@ module.exports = {
          * @private
          */
         function isJSXLiteral(node) {
-            return node.parent.type === "JSXAttribute" || node.parent.type === "JSXElement";
+            return node.parent.type === "JSXAttribute" || node.parent.type === "JSXElement" || node.parent.type === "JSXFragment";
         }
 
         /**


Test patch:
diff --git a/tests/lib/config/config-validator.js b/tests/lib/config/config-validator.js
index 76e573472..456b15183 100644
--- a/tests/lib/config/config-validator.js
+++ b/tests/lib/config/config-validator.js
@@ -362,6 +362,40 @@ describe("Validator", () => {
 
                 assert.throws(fn, "ESLint configuration in tests is invalid:\n\t- Unexpected top-level property \"overrides[0].root\".\n");
             });
+
+            describe("env", () => {
+
+                it("should catch invalid environments", () => {
+                    const fn = validator.validate.bind(null, { overrides: [{ files: "*", env: { browser: true, invalid: true } }] }, null, ruleMapper, linter.environments);
+
+                    assert.throws(fn, "Environment key \"invalid\" is unknown\n");
+                });
+
+                it("should catch disabled invalid environments", () => {
+                    const fn = validator.validate.bind(null, { overrides: [{ files: "*", env: { browser: true, invalid: false } }] }, null, ruleMapper, linter.environments);
+
+                    assert.throws(fn, "Environment key \"invalid\" is unknown\n");
+                });
+
+            });
+
+            describe("rules", () => {
+
+                it("should catch invalid rule options", () => {
+                    const fn = validator.validate.bind(null, { overrides: [{ files: "*", rules: { "mock-rule": [3, "third"] } }] }, "tests", ruleMapper, linter.environments);
+
+                    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 not allow options for rules with no options", () => {
+                    linter.defineRule("mock-no-options-rule", mockNoOptionsRule);
+
+                    const fn = validator.validate.bind(null, { overrides: [{ files: "*", rules: { "mock-no-options-rule": [2, "extra"] } }] }, "tests", ruleMapper, linter.environments);
+
+                    assert.throws(fn, "tests:\n\tConfiguration for rule \"mock-no-options-rule\" is invalid:\n\tValue [\"extra\"] should NOT have more than 0 items.\n");
+                });
+            });
+
         });
 
     });
diff --git a/tests/lib/rules/no-extra-parens.js b/tests/lib/rules/no-extra-parens.js
index 1738b1a66..b3481d06a 100644
--- a/tests/lib/rules/no-extra-parens.js
+++ b/tests/lib/rules/no-extra-parens.js
@@ -288,6 +288,13 @@ ruleTester.run("no-extra-parens", rule, {
             "    );",
             "}"
         ].join("\n"),
+        [
+            "function a() {",
+            "    return (",
+            "        <></>",
+            "    );",
+            "}"
+        ].join("\n"),
         [
             "throw (",
             "    a",
@@ -321,6 +328,14 @@ ruleTester.run("no-extra-parens", rule, {
 
         // ["all", { ignoreJSX: "all" }]
         { code: "const Component = (<div />)", options: ["all", { ignoreJSX: "all" }] },
+        {
+            code: [
+                "const Component = (<>",
+                "  <p />",
+                "</>);"
+            ].join("\n"),
+            options: ["all", { ignoreJSX: "all" }]
+        },
         {
             code: [
                 "const Component = (<div>",
@@ -663,6 +678,19 @@ ruleTester.run("no-extra-parens", rule, {
             "    );",
             "}"
         ].join("\n"), "JSXElement", null),
+        invalid([
+            "function a() {",
+            "    return ((",
+            "       <></>",
+            "    ));",
+            "}"
+        ].join("\n"), [
+            "function a() {",
+            "    return (",
+            "       <></>",
+            "    );",
+            "}"
+        ].join("\n"), "JSXFragment", null),
         invalid("throw (a);", "throw a;", "Identifier"),
         invalid([
             "throw ((",
@@ -884,6 +912,13 @@ ruleTester.run("no-extra-parens", rule, {
         ].join("\n"), "const Component = \n  <div />\n;", "JSXElement", 1, {
             options: ["all", { ignoreJSX: "multi-line" }]
         }),
+        invalid([
+            "const Component = (",
+            "  <></>",
+            ");"
+        ].join("\n"), "const Component = \n  <></>\n;", "JSXFragment", 1, {
+            options: ["all", { ignoreJSX: "multi-line" }]
+        }),
 
         // ["all", { ignoreJSX: "single-line" }]
         invalid([
diff --git a/tests/lib/rules/no-useless-escape.js b/tests/lib/rules/no-useless-escape.js
index b22309956..636149635 100644
--- a/tests/lib/rules/no-useless-escape.js
+++ b/tests/lib/rules/no-useless-escape.js
@@ -50,6 +50,8 @@ ruleTester.run("no-useless-escape", rule, {
         { code: "<div> Testing: \\ </div>", parserOptions: { ecmaFeatures: { jsx: true } } },
         { code: "<div> Testing: &#x5C </div>", parserOptions: { ecmaFeatures: { jsx: true } } },
         { code: "<foo attr='\\d'></foo>", parserOptions: { ecmaFeatures: { jsx: true } } },
+        { code: "<> Testing: \\ </>", parserOptions: { ecmaFeatures: { jsx: true } } },
+        { code: "<> Testing: &#x5C </>", parserOptions: { ecmaFeatures: { jsx: true } } },
         { code: "var foo = `\\x123`", parserOptions: { ecmaVersion: 6 } },
         { code: "var foo = `\\u00a9`", parserOptions: { ecmaVersion: 6 } },
         { code: "var foo = `xs\\u2111`", parserOptions: { ecmaVersion: 6 } },
diff --git a/tests/lib/rules/prefer-const.js b/tests/lib/rules/prefer-const.js
index 85916648f..59865ec86 100644
--- a/tests/lib/rules/prefer-const.js
+++ b/tests/lib/rules/prefer-const.js
@@ -69,6 +69,22 @@ ruleTester.run("prefer-const", rule, {
         "/*exported a*/ let a; function init() { a = foo(); }",
         "/*exported a*/ let a = 1",
         "let a; if (true) a = 0; foo(a);",
+        `
+        (function (a) {
+            let b;
+            ({ a, b } = obj);
+        })();
+        `,
+        `
+        (function (a) {
+            let b;
+            ([ a, b ] = obj);
+        })();
+        `,
+        "var a; { var b; ({ a, b } = obj); }",
+        "let a; { let b; ({ a, b } = obj); }",
+        "var a; { var b; ([ a, b ] = obj); }",
+        "let a; { let b; ([ a, b ] = obj); }",
 
         /*
          * The assignment is located in a different scope.
diff --git a/tests/lib/rules/prefer-object-spread.js b/tests/lib/rules/prefer-object-spread.js
index 75b422f8b..14bfa2f1e 100644
--- a/tests/lib/rules/prefer-object-spread.js
+++ b/tests/lib/rules/prefer-object-spread.js
@@ -14,7 +14,8 @@ const parserOptions = {
     ecmaVersion: 2018,
     ecmaFeatures: {
         experimentalObjectRestSpread: true
-    }
+    },
+    sourceType: "module"
 };
 
 const ruleTester = new RuleTester({ parserOptions });
@@ -45,6 +46,22 @@ ruleTester.run("prefer-object-spread", rule, {
         `
         Object = {};
         Object.assign({ foo: 'bar' });
+        `,
+        `
+        const Object = require('foo');
+        Object.assign({ foo: 'bar' });
+        `,
+        `
+        import Object from 'foo';
+        Object.assign({ foo: 'bar' });
+        `,
+        `
+        import { Something as Object } from 'foo';
+        Object.assign({ foo: 'bar' });
+        `,
+        `
+        import { Object, Array } from 'globals';
+        Object.assign({ foo: 'bar' });
         `
     ],
 
@@ -291,6 +308,9 @@ ruleTester.run("prefer-object-spread", rule, {
                 baz: "cats"
                 --> weird
 }`,
+            parserOptions: {
+                sourceType: "script"
+            },
             errors: [
                 {
                     messageId: "useSpreadMessage",
@@ -572,6 +592,114 @@ ruleTester.run("prefer-object-spread", rule, {
                     column: 30
                 }
             ]
+        },
+        {
+            code: `
+                import Foo from 'foo';
+                Object.assign({ foo: Foo });
+            `,
+            output: `
+                import Foo from 'foo';
+                ({ foo: Foo });
+            `,
+            errors: [
+                {
+                    messageId: "useLiteralMessage",
+                    type: "CallExpression",
+                    line: 3,
+                    column: 17
+                }
+            ]
+        },
+        {
+            code: `
+                import Foo from 'foo';
+                Object.assign({}, Foo);
+            `,
+            output: `
+                import Foo from 'foo';
+                ({...Foo});
+            `,
+            errors: [
+                {
+                    messageId: "useSpreadMessage",
+                    type: "CallExpression",
+                    line: 3,
+                    column: 17
+                }
+            ]
+        },
+        {
+            code: `
+                const Foo = require('foo');
+                Object.assign({ foo: Foo });
+            `,
+            output: `
+                const Foo = require('foo');
+                ({ foo: Foo });
+            `,
+            errors: [
+                {
+                    messageId: "useLiteralMessage",
+                    type: "CallExpression",
+                    line: 3,
+                    column: 17
+                }
+            ]
+        },
+        {
+            code: `
+                import { Something as somethingelse } from 'foo';
+                Object.assign({}, somethingelse);
+            `,
+            output: `
+                import { Something as somethingelse } from 'foo';
+                ({...somethingelse});
+            `,
+            errors: [
+                {
+                    messageId: "useSpreadMessage",
+                    type: "CallExpression",
+                    line: 3,
+                    column: 17
+                }
+            ]
+        },
+        {
+            code: `
+                import { foo } from 'foo';
+                Object.assign({ foo: Foo });
+            `,
+            output: `
+                import { foo } from 'foo';
+                ({ foo: Foo });
+            `,
+            errors: [
+                {
+                    messageId: "useLiteralMessage",
+                    type: "CallExpression",
+                    line: 3,
+                    column: 17
+                }
+            ]
+        },
+        {
+            code: `
+                const Foo = require('foo');
+                Object.assign({}, Foo);
+            `,
+            output: `
+                const Foo = require('foo');
+                ({...Foo});
+            `,
+            errors: [
+                {
+                    messageId: "useSpreadMessage",
+                    type: "CallExpression",
+                    line: 3,
+                    column: 17
+                }
+            ]
         }
     ]
 });
diff --git a/tests/lib/rules/quotes.js b/tests/lib/rules/quotes.js
index b6ea51362..812ea737b 100644
--- a/tests/lib/rules/quotes.js
+++ b/tests/lib/rules/quotes.js
@@ -23,6 +23,10 @@ ruleTester.run("quotes", rule, {
         { code: "var foo = 1;", options: ["double"] },
         { code: "var foo = \"'\";", options: ["single", { avoidEscape: true }] },
         { code: "var foo = '\"';", options: ["double", { avoidEscape: true }] },
+        { code: "var foo = <>Hello world</>;", options: ["single"], parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } },
+        { code: "var foo = <>Hello world</>;", options: ["double"], parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } },
+        { code: "var foo = <>Hello world</>;", options: ["double", { avoidEscape: true }], parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } },
+        { code: "var foo = <>Hello world</>;", options: ["backtick"], parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } },
         { code: "var foo = <div>Hello world</div>;", options: ["single"], parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } },
         { code: "var foo = <div id=\"foo\"></div>;", options: ["single"], parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } },
         { code: "var foo = <div>Hello world</div>;", options: ["double"], parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } },
diff --git a/tests/lib/testers/no-test-runners.js b/tests/lib/testers/no-test-runners.js
index 34d8a2a09..a14ac3bab 100644
--- a/tests/lib/testers/no-test-runners.js
+++ b/tests/lib/testers/no-test-runners.js
@@ -6,8 +6,8 @@
 
 /* global describe, it */
 /* eslint-disable no-global-assign*/
-const assert = require("assert"),
-    RuleTester = require("../../../lib/testers/rule-tester");
+const assert = require("assert");
+const RuleTester = require("../../../lib/testers/rule-tester");
 const tmpIt = it;
 const tmpDescribe = describe;
 
@@ -26,7 +26,7 @@ try {
                 { code: "var foo = bar;", output: "invalid output", errors: 1 }
             ]
         });
-    }, /Output is incorrect\. \(' foo = bar;' === 'invalid output'\)$/);
+    }, new assert.AssertionError({ actual: " foo = bar;", expected: "invalid output", operator: "===" }).message);
 } finally {
     it = tmpIt;
     describe = tmpDescribe;