Repository: eslint/eslint
Pull request #10351 (2018-05-14 17:37:42)

Update: Improves the prefer-object-spread rule by removing extraneous visitors
**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))
[X] 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
[ ] Other, please explain:

<!--
    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)**
- Includes a change to stop warning if the if the file is importing `Object`. 
- Uses `context.getScope()` to determine is `Object` is being overwritten, instead of visiting every assignment and variable declaration node.
- Adds more tests


**Is there anything you'd like reviewers to focus on?**
There may be a better way to get module and scope information, not sure 🤔

cc. @ljharb @ilyavolodin 


ljharb: This might collide a bit with #10347
sharmilajesupaul: it looks like I can't use spreads in my test files:
https://github.com/eslint/eslint/pull/10351/files#diff-6088f3cff25518533d89b2e8a4d395faR57

will update. 
sharmilajesupaul: @platinumazure the only test that will fail when using `sourceType: module` is the test using an html comment. But I can add parser options specifically for that test instead of having to add them for every test using a module. 

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/bin/eslint.js b/bin/eslint.js
index 1255e3d8b..adc07c2e7 100755
--- a/bin/eslint.js
+++ b/bin/eslint.js
@@ -56,7 +56,15 @@ process.once("uncaughtException", err => {
 });
 
 if (useStdIn) {
-    process.exitCode = cli.execute(process.argv, fs.readFileSync(process.stdin.fd, "utf8"));
+
+    /*
+     * Note: `process.stdin.fd` is not used here due to https://github.com/nodejs/node/issues/7439.
+     * Accessing the `process.stdin` property seems to modify the behavior of file descriptor 0, resulting
+     * in an error when stdin is piped in asynchronously.
+     */
+    const STDIN_FILE_DESCRIPTOR = 0;
+
+    process.exitCode = cli.execute(process.argv, fs.readFileSync(STDIN_FILE_DESCRIPTOR, "utf8"));
 } else if (init) {
     const configInit = require("../lib/config/config-initializer");
 
diff --git a/docs/rules/array-element-newline.md b/docs/rules/array-element-newline.md
index 21d957101..6a3a66e1d 100644
--- a/docs/rules/array-element-newline.md
+++ b/docs/rules/array-element-newline.md
@@ -12,6 +12,7 @@ This rule has either a string option:
 
 * `"always"` (default) requires line breaks between array elements
 * `"never"` disallows line breaks between array elements
+* `"consistent"` requires consistent usage of linebreaks between array elements
 
 Or an object option (Requires line breaks if any of properties is satisfied. Otherwise, disallows line breaks):
 
@@ -102,6 +103,69 @@ var e = [
 ];
 ```
 
+### consistent
+
+Examples of **incorrect** code for this rule with the `"consistent"` option:
+
+```js
+/*eslint array-element-newline: ["error", "consistent"]*/
+
+var a = [
+    1, 2,
+    3
+];
+var b = [
+    function foo() {
+        dosomething();
+    }, function bar() {
+        dosomething();
+    },
+    function baz() {
+        dosomething();
+    }
+];
+```
+
+Examples of **correct** code for this rule with the `"consistent"` option:
+
+```js
+/*eslint array-element-newline: ["error", "consistent"]*/
+
+var a = [];
+var b = [1];
+var c = [1, 2];
+var d = [1, 2, 3];
+var e = [
+    1,
+    2
+];
+var f = [
+    1,
+    2,
+    3
+];
+var g = [
+    function foo() {
+        dosomething();
+    }, function bar() {
+        dosomething();
+    }, function baz() {
+        dosomething();
+    }
+];
+var h = [
+    function foo() {
+        dosomething();
+    },
+    function bar() {
+        dosomething();
+    },
+    function baz() {
+        dosomething();
+    }
+];
+```
+
 ### multiline
 
 Examples of **incorrect** code for this rule with the `{ "multiline": true }` option:
diff --git a/lib/config/config-initializer.js b/lib/config/config-initializer.js
index fe42880b4..ab06909d8 100644
--- a/lib/config/config-initializer.js
+++ b/lib/config/config-initializer.js
@@ -504,7 +504,7 @@ function promptUser() {
                 earlyAnswers.styleguide = "airbnb-base";
             }
 
-            const config = getConfigForStyleGuide(earlyAnswers.styleguide, earlyAnswers.installESLint);
+            const config = getConfigForStyleGuide(earlyAnswers.styleguide);
             const modules = getModulesList(config);
 
             return askInstallModules(modules, earlyAnswers.packageJsonExists)
diff --git a/lib/rules/array-element-newline.js b/lib/rules/array-element-newline.js
index 359b8d436..b437c6932 100644
--- a/lib/rules/array-element-newline.js
+++ b/lib/rules/array-element-newline.js
@@ -24,7 +24,7 @@ module.exports = {
             {
                 oneOf: [
                     {
-                        enum: ["always", "never"]
+                        enum: ["always", "never", "consistent"]
                     },
                     {
                         type: "object",
@@ -63,6 +63,7 @@ module.exports = {
          * @returns {{multiline: boolean, minItems: number}} Normalized option object.
          */
         function normalizeOptionValue(providedOption) {
+            let consistent = false;
             let multiline = false;
             let minItems;
 
@@ -72,12 +73,15 @@ module.exports = {
                 minItems = 0;
             } else if (option === "never") {
                 minItems = Number.POSITIVE_INFINITY;
+            } else if (option === "consistent") {
+                consistent = true;
+                minItems = Number.POSITIVE_INFINITY;
             } else {
                 multiline = Boolean(option.multiline);
                 minItems = option.minItems || Number.POSITIVE_INFINITY;
             }
 
-            return { multiline, minItems };
+            return { consistent, multiline, minItems };
         }
 
         /**
@@ -193,11 +197,30 @@ module.exports = {
                     .some(element => element.loc.start.line !== element.loc.end.line);
             }
 
+            const linebreaksCount = node.elements.map((element, i) => {
+                const previousElement = elements[i - 1];
+
+                if (i === 0 || element === null || previousElement === null) {
+                    return false;
+                }
+
+                const commaToken = sourceCode.getFirstTokenBetween(previousElement, element, astUtils.isCommaToken);
+                const lastTokenOfPreviousElement = sourceCode.getTokenBefore(commaToken);
+                const firstTokenOfCurrentElement = sourceCode.getTokenAfter(commaToken);
+
+                return !astUtils.isTokenOnSameLine(lastTokenOfPreviousElement, firstTokenOfCurrentElement);
+            }).filter(isBreak => isBreak === true).length;
+
             const needsLinebreaks = (
                 elements.length >= options.minItems ||
                 (
                     options.multiline &&
                     elementBreak
+                ) ||
+                (
+                    options.consistent &&
+                    linebreaksCount > 0 &&
+                    linebreaksCount < node.elements.length
                 )
             );
 
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/one-var.js b/lib/rules/one-var.js
index 70223dd2c..412fe615d 100644
--- a/lib/rules/one-var.js
+++ b/lib/rules/one-var.js
@@ -293,7 +293,11 @@ module.exports = {
                 const res = [];
 
                 if (previousNode && previousNode.kind === sourceCode.getText(type)) {
-                    res.push(fixer.replaceText(prevSemi, ","));
+                    if (prevSemi.value === ";") {
+                        res.push(fixer.replaceText(prevSemi, ","));
+                    } else {
+                        res.push(fixer.insertTextAfter(prevSemi, ","));
+                    }
                     res.push(fixer.replaceText(type, ""));
                 }
 
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/bin/eslint.js b/tests/bin/eslint.js
index 5fc818f28..75e7c2c4b 100644
--- a/tests/bin/eslint.js
+++ b/tests/bin/eslint.js
@@ -152,6 +152,17 @@ describe("bin/eslint.js", () => {
             }
         );
 
+        it("successfully reads from an asynchronous pipe", () => {
+            const child = runESLint(["--stdin", "--no-eslintrc"]);
+
+            child.stdin.write("var foo = bar;\n");
+            return new Promise(resolve => setTimeout(resolve, 300)).then(() => {
+                child.stdin.write("var baz = qux;\n");
+                child.stdin.end();
+
+                return assertExitCode(child, 0);
+            });
+        });
     });
 
     describe("running on files", () => {
diff --git a/tests/lib/rules/array-element-newline.js b/tests/lib/rules/array-element-newline.js
index f652105fc..c9ddd1605 100644
--- a/tests/lib/rules/array-element-newline.js
+++ b/tests/lib/rules/array-element-newline.js
@@ -53,7 +53,7 @@ ruleTester.run("array-element-newline", rule, {
         { code: "var foo = [// any comment \n1,\n2];", options: ["always"] },
         { code: "var foo = [1,\n2 // any comment\n];", options: ["always"] },
         { code: "var foo = [1,\n2,\n3];", options: ["always"] },
-        { code: "var foo = [\nfunction foo() {\ndosomething();\n},\nfunction bar() {\nosomething();\n}\n];", options: ["always"] },
+        { code: "var foo = [\nfunction foo() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n}\n];", options: ["always"] },
 
         // "never"
         { code: "var foo = [];", options: ["never"] },
@@ -66,6 +66,23 @@ ruleTester.run("array-element-newline", rule, {
         { code: "var foo = [1, (\n2\n), 3];", options: ["never"] },
         { code: "var foo = [\nfunction foo() {\ndosomething();\n}, function bar() {\ndosomething();\n}\n];", options: ["never"] },
 
+        // "consistent"
+        { code: "var foo = [];", options: ["consistent"] },
+        { code: "var foo = [1];", options: ["consistent"] },
+        { code: "var foo = [1, 2];", options: ["consistent"] },
+        { code: "var foo = [1,\n2];", options: ["consistent"] },
+        { code: "var foo = [1, 2, 3];", options: ["consistent"] },
+        { code: "var foo = [1,\n2,\n3];", options: ["consistent"] },
+        { code: "var foo = [1,\n2,\n,\n3];", options: ["consistent"] },
+        { code: "var foo = [1, // any comment\n2];", options: ["consistent"] },
+        { code: "var foo = [/* any comment */ 1, 2];", options: ["consistent"] },
+        { code: "var foo = [1, (\n2\n), 3];", options: ["consistent"] },
+        { code: "var foo = [1,\n(2)\n, 3];", options: ["consistent"] },
+        { code: "var foo = [\nfunction foo() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n}\n];", options: ["consistent"] },
+        { code: "var foo = [\nfunction foo() {\ndosomething();\n}, function bar() {\ndosomething();\n}\n];", options: ["consistent"] },
+        { code: "var foo = [\nfunction foo() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n}];", options: ["consistent"] },
+        { code: "var foo = [\nfunction foo() {\ndosomething();\n}, function bar() {\ndosomething();\n}, function bar() {\ndosomething();\n}];", options: ["consistent"] },
+
         // { multiline: true }
         { code: "var foo = [];", options: [{ multiline: true }] },
         { code: "var foo = [1];", options: [{ multiline: true }] },
@@ -450,6 +467,88 @@ ruleTester.run("array-element-newline", rule, {
             ]
         },
 
+        // "consistent"
+        {
+            code: "var foo = [1,\n2, 3];",
+            output: "var foo = [1,\n2,\n3];",
+            options: ["consistent"],
+            errors: [
+                {
+                    messageId: "missingLineBreak",
+                    line: 2,
+                    column: 3,
+                    endLine: 2,
+                    endColumn: 4
+                }
+            ]
+        },
+        {
+            code: "var foo = [1, 2,\n3];",
+            output: "var foo = [1,\n2,\n3];",
+            options: ["consistent"],
+            errors: [
+                {
+                    messageId: "missingLineBreak",
+                    line: 1,
+                    column: 14,
+                    endLine: 1,
+                    endColumn: 15
+                }
+            ]
+        },
+        {
+            code: "var foo = [1,\n(\n2), 3];",
+            output: "var foo = [1,\n(\n2),\n3];",
+            options: ["consistent"],
+            errors: [
+                {
+                    messageId: "missingLineBreak",
+                    line: 3,
+                    column: 4,
+                    endLine: 3,
+                    endColumn: 5
+                }
+            ]
+        },
+        {
+            code: "var foo = [1,        \t      (\n2\n),\n3];",
+            output: "var foo = [1,\n(\n2\n),\n3];",
+            options: ["consistent"],
+            errors: [
+                {
+                    messageId: "missingLineBreak",
+                    line: 1,
+                    column: 14,
+                    endLine: 1,
+                    endColumn: 29
+                }
+            ]
+        },
+        {
+            code: "var foo = [1, /* any comment */(2),\n3];",
+            output: "var foo = [1, /* any comment */\n(2),\n3];",
+            options: ["consistent"],
+            errors: [
+                {
+                    messageId: "missingLineBreak",
+                    line: 1,
+                    column: 32
+                }
+            ]
+        },
+        {
+            code: "var foo = [\nfunction foo() {\ndosomething();\n},function bar() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n}];",
+            output: "var foo = [\nfunction foo() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n}];",
+            options: ["consistent"],
+            errors: [
+                {
+                    messageId: "missingLineBreak",
+                    line: 4,
+                    column: 3
+                }
+            ]
+        },
+
         // { multiline: true }
         {
             code: "var foo = [1,\n2, 3];",
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/one-var.js b/tests/lib/rules/one-var.js
index 50a2ecf1d..1f6626ad8 100644
--- a/tests/lib/rules/one-var.js
+++ b/tests/lib/rules/one-var.js
@@ -1701,6 +1701,17 @@ ruleTester.run("one-var", rule, {
                 line: 1,
                 column: 41
             }]
+        },
+        {
+            code: "var a\nvar b",
+            output: "var a,\n b",
+            options: ["always"],
+            errors: [{
+                message: "Combine this with the previous 'var' statement.",
+                type: "VariableDeclaration",
+                line: 2,
+                column: 1
+            }]
         }
     ]
 });
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;