Repository: yannickcr/eslint-plugin-react
Pull request #1562 (2017-11-22 10:20:02)

Add a forbid-dom-props rule
A new rule forbid-dom-props was added. It is based on
forbid-component-props but it only forbid property on DOM Node
elements.

This can be useful to enforce some good practices, as not using 'id'
attributes or inline styles.

davazp: I used `dom` as they keyword for this rule as I didn't find any other rule acting only on DOM Nodes, and element refers to both Component and DOM Nodes.  Hopefully it is a good name!

I can imagine a more flexible set of option where you can forbid some properties based on the specific DOM Node, but I preferred to keep it similar to  `forbid-component-props` for now. I guess it can be extended if somebody finds that useful.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 50a1fe47..94350707 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -35,7 +35,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
 * Fix [`no-unused-state`][] to correctly handle arrow function class method ([#1363][] @jackyho112)
 * Fix incompatibility with [`typescript-eslint-parser`](https://github.com/eslint/typescript-eslint-parser) ([#1496][] @timothykang)
 * Fix [`jsx-no-bind`][] to only warn for props and account for variable declaration ([#1444][] [#1395][] [#1417][] @jackyho112)
-* Fix [`no-unused-prop-types`][] to handle props usage in custom prop validators ([#1518][] @petersendidit)
+* Fix [`no-props-unused-props`][] to handle props usage in custom prop validators ([#1518][] @petersendidit)
 * Fix [`prefer-stateless-function`][] to account for `contextTypes` and `defaultProps` ([#1521][] @jomasti)
 * Fix [`jsx-no-comment-textnodes`][] to not warn when using two slashes via html entities at the beginning of a literal ([#1517][] @jomasti)
 * Fix [`default-props-match-prop-types`][] crash ([#1499][] @jomasti)
diff --git a/README.md b/README.md
index 9084d6ad..1151b613 100644
--- a/README.md
+++ b/README.md
@@ -88,6 +88,7 @@ Finally, enable all of the rules that you would like to use.  Use [our preset](#
 * [react/destructuring-assignment](docs/rules/destructuring-assignment.md): Rule enforces consistent usage of destructuring assignment in component
 * [react/display-name](docs/rules/display-name.md): Prevent missing `displayName` in a React component definition
 * [react/forbid-component-props](docs/rules/forbid-component-props.md): Forbid certain props on Components
+* [react/forbid-dom-props](docs/rules/forbid-dom-props.md): Forbid certain props on DOM Nodes
 * [react/forbid-elements](docs/rules/forbid-elements.md): Forbid certain elements
 * [react/forbid-prop-types](docs/rules/forbid-prop-types.md): Forbid certain propTypes
 * [react/forbid-foreign-prop-types](docs/rules/forbid-foreign-prop-types.md): Forbid foreign propTypes
diff --git a/docs/rules/forbid-component-props.md b/docs/rules/forbid-component-props.md
index 424d880f..eb557df8 100644
--- a/docs/rules/forbid-component-props.md
+++ b/docs/rules/forbid-component-props.md
@@ -42,3 +42,8 @@ The following patterns are **not** considered warnings:
 ### `forbid`
 
 An array of strings, with the names of props that are forbidden. The default value of this option is `['className', 'style']`.
+
+
+### Related rules
+
+- [forbid-dom-props](./forbid-dom-props.md)
diff --git a/docs/rules/forbid-dom-props.md b/docs/rules/forbid-dom-props.md
new file mode 100644
index 00000000..8dca2826
--- /dev/null
+++ b/docs/rules/forbid-dom-props.md
@@ -0,0 +1,50 @@
+# Forbid certain props on DOM Nodes (react/forbid-dom-props)
+
+This rule prevents passing of props to elements. This rule only applies to DOM Nodes (e.g. `<div />`) and not Components (e.g. `<Component />`).
+The list of forbidden props can be customized with the `forbid` option.
+
+## Rule Details
+
+This rule checks all JSX elements and verifies that no forbidden props are used
+on DOM Nodes. This rule is off by default.
+
+The following patterns are considered warnings:
+
+```jsx
+// [1, { "forbid": ["id"] }]
+<div id='Joe' />
+```
+
+```jsx
+// [1, { "forbid": ["style"] }]
+<div style={{color: 'red'}} />
+```
+
+The following patterns are **not** considered warnings:
+
+```jsx
+// [1, { "forbid": ["id"] }]
+<Hello id='foo' />
+```
+
+```jsx
+// [1, { "forbid": ["id"] }]
+<Hello id={{color: 'red'}} />
+```
+
+## Rule Options
+
+```js
+...
+"react/forbid-dom-props": [<enabled>, { "forbid": [<string>] }]
+...
+```
+
+### `forbid`
+
+An array of strings, with the names of props that are forbidden. The default value of this option `[]`.
+
+
+### Related rules
+
+- [forbid-component-props](./forbid-component-props.md)
diff --git a/index.js b/index.js
index 6c1a933a..6207992b 100644
--- a/index.js
+++ b/index.js
@@ -8,6 +8,7 @@ const allRules = {
   'destructuring-assignment': require('./lib/rules/destructuring-assignment'),
   'display-name': require('./lib/rules/display-name'),
   'forbid-component-props': require('./lib/rules/forbid-component-props'),
+  'forbid-dom-props': require('./lib/rules/forbid-dom-props'),
   'forbid-elements': require('./lib/rules/forbid-elements'),
   'forbid-prop-types': require('./lib/rules/forbid-prop-types'),
   'forbid-foreign-prop-types': require('./lib/rules/forbid-foreign-prop-types'),
diff --git a/lib/rules/default-props-match-prop-types.js b/lib/rules/default-props-match-prop-types.js
index 9f0bda4a..530040d8 100644
--- a/lib/rules/default-props-match-prop-types.js
+++ b/lib/rules/default-props-match-prop-types.js
@@ -40,6 +40,25 @@ module.exports = {
     const allowRequiredDefaults = configuration.allowRequiredDefaults || false;
     const propWrapperFunctions = new Set(context.settings.propWrapperFunctions || []);
 
+    /**
+     * Find a variable by name in the current scope.
+     * @param  {string} name Name of the variable to look for.
+     * @returns {ASTNode|null} Return null if the variable could not be found, ASTNode otherwise.
+     */
+    function findVariableByName(name) {
+      const variable = variableUtil.variablesInScope(context).find(item => item.name === name);
+
+      if (!variable || !variable.defs[0] || !variable.defs[0].node) {
+        return null;
+      }
+
+      if (variable.defs[0].node.type === 'TypeAlias') {
+        return variable.defs[0].node.right;
+      }
+
+      return variable.defs[0].node.init;
+    }
+
     /**
      * Try to resolve the node passed in to a variable in the current scope. If the node passed in is not
      * an Identifier, then the node is simply returned.
@@ -48,7 +67,7 @@ module.exports = {
      */
     function resolveNodeValue(node) {
       if (node.type === 'Identifier') {
-        return variableUtil.findVariableByName(context, node.name);
+        return findVariableByName(node.name);
       }
       if (
         node.type === 'CallExpression' &&
@@ -70,7 +89,7 @@ module.exports = {
         return null;
       }
 
-      return variableUtil.findVariableByName(context, node.id.name);
+      return findVariableByName(node.id.name);
     }
 
     function resolveUnionTypeAnnotation(node) {
@@ -110,7 +129,7 @@ module.exports = {
         annotation = resolveGenericTypeAnnotation(type);
 
         if (annotation && annotation.id) {
-          annotation = variableUtil.findVariableByName(context, annotation.id.name);
+          annotation = findVariableByName(annotation.id.name);
         }
 
         if (!annotation || !annotation.properties) {
@@ -137,7 +156,7 @@ module.exports = {
             properties = getPropertiesFromIntersectionTypeAnnotationNode(annotation);
           } else {
             if (annotation && annotation.id) {
-              annotation = variableUtil.findVariableByName(context, annotation.id.name);
+              annotation = findVariableByName(annotation.id.name);
             }
 
             properties = annotation ? (annotation.properties || []) : [];
diff --git a/lib/rules/forbid-dom-props.js b/lib/rules/forbid-dom-props.js
new file mode 100644
index 00000000..324f6f27
--- /dev/null
+++ b/lib/rules/forbid-dom-props.js
@@ -0,0 +1,70 @@
+/**
+ * @fileoverview Forbid certain props on DOM Nodes
+ * @author David Vázquez
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Constants
+// ------------------------------------------------------------------------------
+
+const DEFAULTS = [];
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Forbid certain props on DOM Nodes',
+      category: 'Best Practices',
+      recommended: false
+    },
+
+    schema: [{
+      type: 'object',
+      properties: {
+        forbid: {
+          type: 'array',
+          items: {
+            type: 'string',
+            minLength: 1
+          },
+          uniqueItems: true
+        }
+      },
+      additionalProperties: false
+    }]
+  },
+
+  create: function(context) {
+    function isForbidden(prop) {
+      const configuration = context.options[0] || {};
+
+      const forbid = configuration.forbid || DEFAULTS;
+      return forbid.indexOf(prop) >= 0;
+    }
+
+    return {
+      JSXAttribute: function(node) {
+        const tag = node.parent.name.name;
+        if (!(tag && tag[0] !== tag[0].toUpperCase())) {
+          // This is a Component, not  a DOM node, so exit.
+          return;
+        }
+
+        const prop = node.name.name;
+
+        if (!isForbidden(prop)) {
+          return;
+        }
+
+        context.report({
+          node: node,
+          message: `Prop \`${prop}\` is forbidden on DOM Nodes`
+        });
+      }
+    };
+  }
+};
diff --git a/lib/rules/forbid-prop-types.js b/lib/rules/forbid-prop-types.js
index 11c275f0..c2ec47c7 100644
--- a/lib/rules/forbid-prop-types.js
+++ b/lib/rules/forbid-prop-types.js
@@ -70,6 +70,26 @@ module.exports = {
       return false;
     }
 
+    /**
+     * Find a variable by name in the current scope.
+     * @param  {string} name Name of the variable to look for.
+     * @returns {ASTNode|null} Return null if the variable could not be found, ASTNode otherwise.
+     */
+    function findVariableByName(name) {
+      const variable = variableUtil.variablesInScope(context).find(item => item.name === name);
+
+      if (!variable || !variable.defs[0] || !variable.defs[0].node) {
+        return null;
+      }
+
+      if (variable.defs[0].node.type === 'TypeAlias') {
+        return variable.defs[0].node.right;
+      }
+
+      return variable.defs[0].node.init;
+    }
+
+
     /**
      * Checks if propTypes declarations are forbidden
      * @param {Array} declarations The array of AST nodes being checked.
@@ -116,7 +136,7 @@ module.exports = {
           checkProperties(node.properties);
           break;
         case 'Identifier':
-          const propTypesObject = variableUtil.findVariableByName(context, node.name);
+          const propTypesObject = findVariableByName(node.name);
           if (propTypesObject && propTypesObject.properties) {
             checkProperties(propTypesObject.properties);
           }
diff --git a/lib/rules/jsx-curly-spacing.js b/lib/rules/jsx-curly-spacing.js
index e67a229d..320395c8 100644
--- a/lib/rules/jsx-curly-spacing.js
+++ b/lib/rules/jsx-curly-spacing.js
@@ -148,33 +148,7 @@ module.exports = {
      * @returns {boolean} Whether or not there is a newline between the tokens.
      */
     function isMultiline(left, right) {
-      return left.loc.end.line !== right.loc.start.line;
-    }
-
-    /**
-     * Trims text of whitespace between two ranges
-     * @param {Fixer} fixer - the eslint fixer object
-     * @param {Location} fromLoc - the start location
-     * @param {Location} toLoc - the end location
-     * @param {string} mode - either 'start' or 'end'
-     * @param {string=} spacing - a spacing value that will optionally add a space to the removed text
-     * @returns {Object|*|{range, text}}
-     */
-    function fixByTrimmingWhitespace(fixer, fromLoc, toLoc, mode, spacing) {
-      let replacementText = sourceCode.text.slice(fromLoc, toLoc);
-      if (mode === 'start') {
-        replacementText = replacementText.replace(/^\s+/gm, '');
-      } else {
-        replacementText = replacementText.replace(/\s+$/gm, '');
-      }
-      if (spacing === SPACING.always) {
-        if (mode === 'start') {
-          replacementText += ' ';
-        } else {
-          replacementText = ` ${replacementText}`;
-        }
-      }
-      return fixer.replaceTextRange([fromLoc, toLoc], replacementText);
+      return left.loc.start.line !== right.loc.start.line;
     }
 
     /**
@@ -190,7 +164,7 @@ module.exports = {
         message: `There should be no newline after '${token.value}'`,
         fix: function(fixer) {
           const nextToken = sourceCode.getTokenAfter(token);
-          return fixByTrimmingWhitespace(fixer, token.range[1], nextToken.range[0], 'start', spacing);
+          return fixer.replaceTextRange([token.range[1], nextToken.range[0]], spacing === SPACING.always ? ' ' : '');
         }
       });
     }
@@ -208,7 +182,7 @@ module.exports = {
         message: `There should be no newline before '${token.value}'`,
         fix: function(fixer) {
           const previousToken = sourceCode.getTokenBefore(token);
-          return fixByTrimmingWhitespace(fixer, previousToken.range[1], token.range[0], 'end', spacing);
+          return fixer.replaceTextRange([previousToken.range[1], token.range[0]], spacing === SPACING.always ? ' ' : '');
         }
       });
     }
@@ -226,7 +200,10 @@ module.exports = {
         message: `There should be no space after '${token.value}'`,
         fix: function(fixer) {
           const nextToken = sourceCode.getTokenAfter(token);
-          return fixByTrimmingWhitespace(fixer, token.range[1], nextToken.range[0], 'start');
+          const nextNode = sourceCode.getNodeByRangeIndex(nextToken.range[0]);
+          const leadingComments = sourceCode.getComments(nextNode).leading;
+          const rangeEndRef = leadingComments.length ? leadingComments[0] : nextToken;
+          return fixer.removeRange([token.range[1], rangeEndRef.range[0]]);
         }
       });
     }
@@ -244,7 +221,10 @@ module.exports = {
         message: `There should be no space before '${token.value}'`,
         fix: function(fixer) {
           const previousToken = sourceCode.getTokenBefore(token);
-          return fixByTrimmingWhitespace(fixer, previousToken.range[1], token.range[0], 'end');
+          const previousNode = sourceCode.getNodeByRangeIndex(previousToken.range[0]);
+          const trailingComments = sourceCode.getComments(previousNode).trailing;
+          const rangeStartRef = trailingComments.length ? trailingComments[trailingComments.length - 1] : previousToken;
+          return fixer.removeRange([rangeStartRef.range[1], token.range[0]]);
         }
       });
     }
diff --git a/lib/rules/no-unused-prop-types.js b/lib/rules/no-unused-prop-types.js
index d4135140..f0c85596 100644
--- a/lib/rules/no-unused-prop-types.js
+++ b/lib/rules/no-unused-prop-types.js
@@ -780,10 +780,6 @@ module.exports = {
           return declarePropTypesForObjectTypeAnnotation(annotation, declaredPropTypes);
         }
 
-        if (annotation.type === 'UnionTypeAnnotation') {
-          return true;
-        }
-
         const typeNode = typeScope(annotation.id.name);
 
         if (!typeNode) {
diff --git a/lib/rules/prop-types.js b/lib/rules/prop-types.js
index 16d09d8e..7638959d 100644
--- a/lib/rules/prop-types.js
+++ b/lib/rules/prop-types.js
@@ -726,10 +726,6 @@ module.exports = {
           return declarePropTypesForObjectTypeAnnotation(annotation, declaredPropTypes);
         }
 
-        if (annotation.type === 'UnionTypeAnnotation') {
-          return true;
-        }
-
         const typeNode = typeScope(annotation.id.name);
 
         if (!typeNode) {
diff --git a/lib/rules/require-default-props.js b/lib/rules/require-default-props.js
index 505b1be3..46b228b4 100644
--- a/lib/rules/require-default-props.js
+++ b/lib/rules/require-default-props.js
@@ -41,6 +41,25 @@ module.exports = {
     const configuration = context.options[0] || {};
     const forbidDefaultForRequired = configuration.forbidDefaultForRequired || false;
 
+    /**
+     * Find a variable by name in the current scope.
+     * @param  {string} name Name of the variable to look for.
+     * @returns {ASTNode|null} Return null if the variable could not be found, ASTNode otherwise.
+     */
+    function findVariableByName(name) {
+      const variable = variableUtil.variablesInScope(context).find(item => item.name === name);
+
+      if (!variable || !variable.defs[0] || !variable.defs[0].node) {
+        return null;
+      }
+
+      if (variable.defs[0].node.type === 'TypeAlias') {
+        return variable.defs[0].node.right;
+      }
+
+      return variable.defs[0].node.init;
+    }
+
     /**
      * Try to resolve the node passed in to a variable in the current scope. If the node passed in is not
      * an Identifier, then the node is simply returned.
@@ -49,7 +68,7 @@ module.exports = {
      */
     function resolveNodeValue(node) {
       if (node.type === 'Identifier') {
-        return variableUtil.findVariableByName(context, node.name);
+        return findVariableByName(node.name);
       }
       if (
         node.type === 'CallExpression' &&
@@ -72,7 +91,7 @@ module.exports = {
         return null;
       }
 
-      return variableUtil.findVariableByName(context, node.id.name);
+      return findVariableByName(node.id.name);
     }
 
     function resolveUnionTypeAnnotation(node) {
@@ -114,7 +133,7 @@ module.exports = {
           let annotation = resolveGenericTypeAnnotation(node.typeAnnotation);
 
           if (annotation && annotation.id) {
-            annotation = variableUtil.findVariableByName(context, annotation.id.name);
+            annotation = findVariableByName(annotation.id.name);
           }
 
           properties = annotation ? (annotation.properties || []) : [];
diff --git a/lib/rules/sort-prop-types.js b/lib/rules/sort-prop-types.js
index 1e48b954..b7b57d21 100644
--- a/lib/rules/sort-prop-types.js
+++ b/lib/rules/sort-prop-types.js
@@ -69,6 +69,25 @@ module.exports = {
       );
     }
 
+    /**
+     * Find a variable by name in the current scope.
+     * @param  {string} name Name of the variable to look for.
+     * @returns {ASTNode|null} Return null if the variable could not be found, ASTNode otherwise.
+     */
+    function findVariableByName(name) {
+      const variable = variableUtil.variablesInScope(context).find(item => item.name === name);
+
+      if (!variable || !variable.defs[0] || !variable.defs[0].node) {
+        return null;
+      }
+
+      if (variable.defs[0].node.type === 'TypeAlias') {
+        return variable.defs[0].node.right;
+      }
+
+      return variable.defs[0].node.init;
+    }
+
     /**
      * Checks if propTypes declarations are sorted
      * @param {Array} declarations The array of AST nodes being checked.
@@ -140,7 +159,7 @@ module.exports = {
           checkSorted(node.properties);
           break;
         case 'Identifier':
-          const propTypesObject = variableUtil.findVariableByName(context, node.name);
+          const propTypesObject = findVariableByName(node.name);
           if (propTypesObject && propTypesObject.properties) {
             checkSorted(propTypesObject.properties);
           }
diff --git a/lib/util/variable.js b/lib/util/variable.js
index 30137c83..367b613a 100644
--- a/lib/util/variable.js
+++ b/lib/util/variable.js
@@ -51,29 +51,8 @@ function variablesInScope(context) {
   return variables;
 }
 
-/**
- * Find a variable by name in the current scope.
- * @param {Object} context The current rule context.
- * @param  {string} name Name of the variable to look for.
- * @returns {ASTNode|null} Return null if the variable could not be found, ASTNode otherwise.
- */
-function findVariableByName(context, name) {
-  const variable = getVariable(variablesInScope(context), name);
-
-  if (!variable || !variable.defs[0] || !variable.defs[0].node) {
-    return null;
-  }
-
-  if (variable.defs[0].node.type === 'TypeAlias') {
-    return variable.defs[0].node.right;
-  }
-
-  return variable.defs[0].node.init;
-}
-
 module.exports = {
   findVariable: findVariable,
-  findVariableByName: findVariableByName,
   getVariable: getVariable,
   variablesInScope: variablesInScope
 };


Test patch:
diff --git a/tests/lib/rules/forbid-dom-props.js b/tests/lib/rules/forbid-dom-props.js
new file mode 100644
index 00000000..0e2b021e
--- /dev/null
+++ b/tests/lib/rules/forbid-dom-props.js
@@ -0,0 +1,132 @@
+/**
+ * @fileoverview Tests for forbid-dom-props
+ */
+'use strict';
+
+// -----------------------------------------------------------------------------
+// Requirements
+// -----------------------------------------------------------------------------
+
+const rule = require('../../../lib/rules/forbid-dom-props');
+const RuleTester = require('eslint').RuleTester;
+
+const parserOptions = {
+  ecmaVersion: 8,
+  sourceType: 'module',
+  ecmaFeatures: {
+    experimentalObjectRestSpread: true,
+    jsx: true
+  }
+};
+
+require('babel-eslint');
+
+// -----------------------------------------------------------------------------
+// Tests
+// -----------------------------------------------------------------------------
+
+const ID_ERROR_MESSAGE = 'Prop `id` is forbidden on DOM Nodes';
+
+const ruleTester = new RuleTester({parserOptions});
+ruleTester.run('forbid-element-props', rule, {
+
+  valid: [{
+    code: [
+      'var First = createReactClass({',
+      '  render: function() {',
+      '    return <Foo id="foo" />;',
+      '  }',
+      '});'
+    ].join('\n'),
+    options: [{forbid: ['id']}]
+  }, {
+    code: [
+      'var First = createReactClass({',
+      '  propTypes: externalPropTypes,',
+      '  render: function() {',
+      '    return <Foo id="bar" style={{color: "red"}} />;',
+      '  }',
+      '});'
+    ].join('\n'),
+    options: [{forbid: ['style', 'id']}]
+  }, {
+    code: [
+      'var First = createReactClass({',
+      '  propTypes: externalPropTypes,',
+      '  render: function() {',
+      '    return <this.Foo bar="baz" />;',
+      '  }',
+      '});'
+    ].join('\n'),
+    options: [{forbid: ['id']}]
+  }, {
+    code: [
+      'class First extends createReactClass {',
+      '  render() {',
+      '    return <this.foo id="bar" />;',
+      '  }',
+      '}'
+    ].join('\n'),
+    options: [{forbid: ['id']}]
+  }, {
+    code: [
+      'const First = (props) => (',
+      '  <this.Foo {...props} />',
+      ');'
+    ].join('\n'),
+    options: [{forbid: ['id']}]
+  }, {
+    code: [
+      'const First = (props) => (',
+      '  <div name="foo" />',
+      ');'
+    ].join('\n'),
+    options: [{forbid: ['id']}]
+  }],
+
+  invalid: [{
+    code: [
+      'var First = createReactClass({',
+      '  propTypes: externalPropTypes,',
+      '  render: function() {',
+      '    return <div id="bar" />;',
+      '  }',
+      '});'
+    ].join('\n'),
+    options: [{forbid: ['id']}],
+    errors: [{
+      message: ID_ERROR_MESSAGE,
+      line: 4,
+      column: 17,
+      type: 'JSXAttribute'
+    }]
+  }, {
+    code: [
+      'class First extends createReactClass {',
+      '  render() {',
+      '    return <div id="bar" />;',
+      '  }',
+      '}'
+    ].join('\n'),
+    options: [{forbid: ['id']}],
+    errors: [{
+      message: ID_ERROR_MESSAGE,
+      line: 3,
+      column: 17,
+      type: 'JSXAttribute'
+    }]
+  }, {
+    code: [
+      'const First = (props) => (',
+      '  <div id="foo" />',
+      ');'
+    ].join('\n'),
+    options: [{forbid: ['id']}],
+    errors: [{
+      message: ID_ERROR_MESSAGE,
+      line: 2,
+      column: 8,
+      type: 'JSXAttribute'
+    }]
+  }]
+});
diff --git a/tests/lib/rules/jsx-curly-spacing.js b/tests/lib/rules/jsx-curly-spacing.js
index d42ce7b0..17fbcf1e 100644
--- a/tests/lib/rules/jsx-curly-spacing.js
+++ b/tests/lib/rules/jsx-curly-spacing.js
@@ -671,13 +671,6 @@ ruleTester.run('jsx-curly-spacing', rule, {
   }, {
     code: '<App foo={ bar }>{bar}</App>',
     options: ['always']
-  }, {
-    code: [
-      '<App>{`',
-      'text',
-      '`}</App>'
-    ].join('\n'),
-    options: [{children: {when: 'never', allowMultiline: false}}]
   }],
 
   invalid: [{
@@ -2125,60 +2118,5 @@ ruleTester.run('jsx-curly-spacing', rule, {
     }, {
       message: 'A space is required before \'}\''
     }]
-  }, {
-    code: '<App>{/*comment*/ }</App>',
-    output: '<App>{/*comment*/}</App>',
-    options: [{children: {when: 'never'}}],
-    errors: [{
-      message: 'There should be no space before \'}\''
-    }]
-  }, {
-    code: '<App>{ /*comment*/}</App>',
-    output: '<App>{/*comment*/}</App>',
-    options: [{children: {when: 'never'}}],
-    errors: [{
-      message: 'There should be no space after \'{\''
-    }]
-  }, {
-    code: '<App>{/*comment*/}</App>',
-    output: '<App>{ /*comment*/ }</App>',
-    options: [{children: {when: 'always'}}],
-    errors: [{
-      message: 'A space is required after \'{\''
-    }, {
-      message: 'A space is required before \'}\''
-    }]
-  }, {
-    code: [
-      '<App>',
-      '{/*comment*/',
-      '}',
-      '</App>'
-    ].join('\n'),
-    output: [
-      '<App>',
-      '{/*comment*/}',
-      '</App>'
-    ].join('\n'),
-    options: [{children: {when: 'never', allowMultiline: false}}],
-    errors: [{
-      message: 'There should be no newline before \'}\''
-    }]
-  }, {
-    code: [
-      '<App>',
-      '{',
-      '/*comment*/}',
-      '</App>'
-    ].join('\n'),
-    output: [
-      '<App>',
-      '{/*comment*/}',
-      '</App>'
-    ].join('\n'),
-    options: [{children: {when: 'never', allowMultiline: false}}],
-    errors: [{
-      message: 'There should be no newline after \'{\''
-    }]
   }]
 });
diff --git a/tests/lib/rules/no-unused-prop-types.js b/tests/lib/rules/no-unused-prop-types.js
index 97ce57be..d4427aab 100644
--- a/tests/lib/rules/no-unused-prop-types.js
+++ b/tests/lib/rules/no-unused-prop-types.js
@@ -1097,28 +1097,6 @@ ruleTester.run('no-unused-prop-types', rule, {
         '}'
       ].join('\n'),
       parser: 'babel-eslint'
-    }, {
-      code: [
-        'type PropsUnionA = {',
-        '  a: string,',
-        '  b?: void,',
-        '};',
-        'type PropsUnionB = {',
-        '  a?: void,',
-        '  b: string,',
-        '};',
-        'type Props = {',
-        '  name: string,',
-        '} & (PropsUnionA | PropsUnionB);',
-        'class Hello extends React.Component {',
-        '  props: Props;',
-        '  render() {',
-        '    const {name} = this.props;',
-        '    return name;',
-        '  }',
-        '}'
-      ].join('\n'),
-      parser: 'babel-eslint'
     }, {
       code: [
         'Card.propTypes = {',
diff --git a/tests/lib/rules/prop-types.js b/tests/lib/rules/prop-types.js
index c16b7065..9bc4223e 100644
--- a/tests/lib/rules/prop-types.js
+++ b/tests/lib/rules/prop-types.js
@@ -1129,28 +1129,6 @@ ruleTester.run('prop-types', rule, {
         '}'
       ].join('\n'),
       parser: 'babel-eslint'
-    }, {
-      code: [
-        'type PropsUnionA = {',
-        '  a: string,',
-        '  b?: void,',
-        '};',
-        'type PropsUnionB = {',
-        '  a?: void,',
-        '  b: string,',
-        '};',
-        'type Props = {',
-        '  name: string,',
-        '} & (PropsUnionA | PropsUnionB);',
-        'class Hello extends React.Component {',
-        '  props: Props;',
-        '  render() {',
-        '    const {name} = this.props;',
-        '    return name;',
-        '  }',
-        '}'
-      ].join('\n'),
-      parser: 'babel-eslint'
     }, {
       code: [
         'Card.propTypes = {',