Skip to content

Commit

Permalink
Add some more test cases and refactor
Browse files Browse the repository at this point in the history
This adds a couple more test cases that were missed. This also refactors
the rule a bit to clean it up. There is also a workaround for keeping
track of TypeAlias nodes seen to work around an issue with eslint@3 and
babel-eslint preventing the TypeAlias variables being found in the
scope.
  • Loading branch information
jomasti committed Jan 4, 2019
1 parent eae169b commit ecbb05d
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 29 deletions.
74 changes: 45 additions & 29 deletions lib/rules/prefer-exact-props.js
Expand Up @@ -28,7 +28,9 @@ module.exports = {
},

create: Components.detect((context, components, utils) => {
const typeAliases = {};
const exactWrappers = propWrapperUtil.getExactPropWrapperFunctions(context);
const sourceCode = context.getSourceCode();

function getPropTypesErrorMessage() {
const formattedWrappers = propWrapperUtil.formatPropWrapperFunctions(exactWrappers);
Expand Down Expand Up @@ -71,23 +73,46 @@ module.exports = {
);
}

function isNonExactPropWrapperFunction(node) {
return (
node &&
node.type === 'CallExpression' &&
!propWrapperUtil.isExactPropWrapperFunction(context, sourceCode.getText(node.callee))
);
}

function reportPropTypesError(node) {
context.report({
node: node,
message: PROP_TYPES_MESSAGE,
data: getPropTypesErrorMessage()
});
}

function reportFlowError(node) {
context.report({
node: node,
message: FLOW_MESSAGE
});
}

return {
TypeAlias: function(node) {
// working around an issue with eslint@3 and babel-eslint not finding the TypeAlias in scope
typeAliases[node.id.name] = node;
},

ClassProperty: function(node) {
if (!propsUtil.isPropTypesDeclaration(node)) {
return;
}

if (hasNonExactObjectTypeAnnotation(node)) {
context.report({
node: node,
message: FLOW_MESSAGE
});
} else if (isNonEmptyObjectExpression(node.value) && exactWrappers.size > 0) {
context.report({
node: node,
message: PROP_TYPES_MESSAGE,
data: getPropTypesErrorMessage()
});
reportFlowError(node);
} else if (exactWrappers.size > 0 && isNonEmptyObjectExpression(node.value)) {
reportPropTypesError(node);
} else if (exactWrappers.size > 0 && isNonExactPropWrapperFunction(node.value)) {
reportPropTypesError(node);
}
},

Expand All @@ -97,18 +122,13 @@ module.exports = {
}

if (hasNonExactObjectTypeAnnotation(node)) {
context.report({
node: node,
message: FLOW_MESSAGE
});
reportFlowError(node);
} else if (hasGenericTypeAnnotation(node)) {
const identifier = node.typeAnnotation.typeAnnotation.id.name;
const propsDefinition = variableUtil.findVariableByName(context, identifier);
const typeAlias = typeAliases[identifier];
const propsDefinition = typeAlias ? typeAlias.right : null;
if (isNonExactObjectTypeAnnotation(propsDefinition)) {
context.report({
node: node,
message: FLOW_MESSAGE
});
reportFlowError(node);
}
}
},
Expand All @@ -120,20 +140,16 @@ module.exports = {

const right = node.parent.right;
if (isNonEmptyObjectExpression(right)) {
context.report({
node: node,
message: PROP_TYPES_MESSAGE,
data: getPropTypesErrorMessage()
});
reportPropTypesError(node);
} else if (isNonExactPropWrapperFunction(right)) {
reportPropTypesError(node);
} else if (right.type === 'Identifier') {
const identifier = right.name;
const propsDefinition = variableUtil.findVariableByName(context, identifier);
if (isNonEmptyObjectExpression(propsDefinition)) {
context.report({
node: node,
message: PROP_TYPES_MESSAGE,
data: getPropTypesErrorMessage()
});
reportPropTypesError(node);
} else if (isNonExactPropWrapperFunction(propsDefinition)) {
reportPropTypesError(node);
}
}
}
Expand Down
105 changes: 105 additions & 0 deletions tests/lib/rules/prefer-exact-props.js
Expand Up @@ -186,6 +186,39 @@ ruleTester.run('prefer-exact-props', rule, {
bar: PropTypes.string,
};
`
}, {
code: `
import somethingElse from "something-else";
const props = {
foo: PropTypes.string,
bar: PropTypes.shape({
baz: PropTypes.string
})
};
class Component extends React.Component {
render() {
return <div />;
}
}
Component.propTypes = somethingElse(props);
`,
}, {
code: `
import somethingElse from "something-else";
const props =
class Component extends React.Component {
static propTypes = somethingElse({
foo: PropTypes.string,
bar: PropTypes.shape({
baz: PropTypes.string
})
});
render() {
return <div />;
}
}
`,
parser: 'babel-eslint'
}],
invalid: [{
code: `
Expand Down Expand Up @@ -291,5 +324,77 @@ ruleTester.run('prefer-exact-props', rule, {
]
},
errors: [{message: 'Component propTypes should be exact by using one of \'exact\', \'forbidExtraProps\'.'}]
}, {
code: `
const props = {
foo: PropTypes.string,
bar: PropTypes.shape({
baz: PropTypes.string
})
};
class Component extends React.Component {
render() {
return <div />;
}
}
Component.propTypes = props;
`,
settings: {
propWrapperFunctions: [
{property: 'exact', exact: true},
{property: 'forbidExtraProps', exact: true}
]
},
errors: [{message: 'Component propTypes should be exact by using one of \'exact\', \'forbidExtraProps\'.'}]
}, {
code: `
import somethingElse from "something-else";
function Component({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
Component.propTypes = somethingElse({
foo: PropTypes.string,
bar: PropTypes.string,
});
`,
settings: settings,
errors: [{message: 'Component propTypes should be exact by using \'exact\'.'}]
}, {
code: `
import somethingElse from "something-else";
const props = {
foo: PropTypes.string,
bar: PropTypes.shape({
baz: PropTypes.string
})
};
class Component extends React.Component {
render() {
return <div />;
}
}
Component.propTypes = somethingElse(props);
`,
settings: settings,
errors: [{message: 'Component propTypes should be exact by using \'exact\'.'}]
}, {
code: `
import somethingElse from "something-else";
const props =
class Component extends React.Component {
static propTypes = somethingElse({
foo: PropTypes.string,
bar: PropTypes.shape({
baz: PropTypes.string
})
});
render() {
return <div />;
}
}
`,
settings: settings,
parser: 'babel-eslint',
errors: [{message: 'Component propTypes should be exact by using \'exact\'.'}],
}]
});

0 comments on commit ecbb05d

Please sign in to comment.