Skip to content

Commit fb5c87f

Browse files
committed
Additional tests for prop-types and no-unused-prop-types
1 parent fb745ab commit fb5c87f

File tree

5 files changed

+332
-38
lines changed

5 files changed

+332
-38
lines changed

lib/rules/no-unused-prop-types.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ module.exports = {
146146
function mustBeValidated(component) {
147147
return Boolean(
148148
component &&
149-
!component.ignorePropsValidation
149+
!component.ignoreUnusedPropTypesValidation
150150
);
151151
}
152152

@@ -397,7 +397,7 @@ module.exports = {
397397

398398
const component = components.get(utils.getParentComponent());
399399
const usedPropTypes = component && component.usedPropTypes || [];
400-
let ignorePropsValidation = component && component.ignorePropsValidation || false;
400+
let ignoreUnusedPropTypesValidation = component && component.ignoreUnusedPropTypesValidation || false;
401401

402402
switch (type) {
403403
case 'direct':
@@ -414,7 +414,7 @@ module.exports = {
414414
case 'destructuring':
415415
for (let k = 0, l = (properties || []).length; k < l; k++) {
416416
if (hasSpreadOperator(properties[k]) || properties[k].computed) {
417-
ignorePropsValidation = true;
417+
ignoreUnusedPropTypesValidation = true;
418418
break;
419419
}
420420
const propName = getKeyValue(properties[k]);
@@ -441,7 +441,7 @@ module.exports = {
441441

442442
components.set(component ? component.node : node, {
443443
usedPropTypes: usedPropTypes,
444-
ignorePropsValidation: ignorePropsValidation
444+
ignoreUnusedPropTypesValidation: ignoreUnusedPropTypesValidation
445445
});
446446
}
447447

lib/rules/prop-types.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ module.exports = {
191191
return true;
192192
}
193193
// Consider every children as declared
194-
if (propType.children === true) {
194+
if (propType.children === true || propType.containsSpread) {
195195
return true;
196196
}
197197
if (propType.acceptedProperties) {

lib/util/propTypes.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,10 @@ module.exports = function propTypesInstructions(context, components, utils) {
143143
}
144144
});
145145

146-
// nested object type spread means we need to ignore/accept everything in this object
147-
if (containsObjectTypeSpread) {
148-
return {};
149-
}
146+
// Mark if this shape has spread. We will know to consider all props from this shape as having propTypes,
147+
// but still have the ability to detect unused children of this shape.
148+
shapeTypeDefinition.containsSpread = containsObjectTypeSpread;
149+
150150
return shapeTypeDefinition;
151151
},
152152

@@ -669,7 +669,7 @@ module.exports = function propTypesInstructions(context, components, utils) {
669669
JSXSpreadAttribute: function(node) {
670670
const component = components.get(utils.getParentComponent());
671671
components.set(component ? component.node : node, {
672-
ignorePropsValidation: true
672+
ignoreUnusedPropTypesValidation: true
673673
});
674674
},
675675

tests/lib/rules/no-unused-prop-types.js

+230-28
Original file line numberDiff line numberDiff line change
@@ -1801,34 +1801,6 @@ ruleTester.run('no-unused-prop-types', rule, {
18011801
' bar: PropTypes.bool',
18021802
'};'
18031803
].join('\n')
1804-
}, {
1805-
code: [
1806-
'type Person = {',
1807-
' ...data,',
1808-
' lastname: string',
1809-
'};',
1810-
'class Hello extends React.Component {',
1811-
' props: Person;',
1812-
' render () {',
1813-
' return <div>Hello {this.props.firstname}</div>;',
1814-
' }',
1815-
'}'
1816-
].join('\n'),
1817-
parser: 'babel-eslint'
1818-
}, {
1819-
code: [
1820-
'type Person = {|',
1821-
' ...data,',
1822-
' lastname: string',
1823-
'|};',
1824-
'class Hello extends React.Component {',
1825-
' props: Person;',
1826-
' render () {',
1827-
' return <div>Hello {this.props.firstname}</div>;',
1828-
' }',
1829-
'}'
1830-
].join('\n'),
1831-
parser: 'babel-eslint'
18321804
}, {
18331805
// The next two test cases are related to: https://github.com/yannickcr/eslint-plugin-react/issues/1183
18341806
code: [
@@ -2470,6 +2442,20 @@ ruleTester.run('no-unused-prop-types', rule, {
24702442
'}'
24712443
].join('\n'),
24722444
parser: 'babel-eslint'
2445+
}, {
2446+
code: [
2447+
'const foo = {};',
2448+
'class Hello extends React.Component {',
2449+
' render() {',
2450+
' const {firstname, lastname} = this.props.name;',
2451+
' return <div>{firstname} {lastname}</div>;',
2452+
' }',
2453+
'}',
2454+
'Hello.propTypes = {',
2455+
' name: PropTypes.shape(foo)',
2456+
'};'
2457+
].join('\n'),
2458+
parser: 'babel-eslint'
24732459
}, {
24742460
// issue #933
24752461
code: [
@@ -2913,6 +2899,23 @@ ruleTester.run('no-unused-prop-types', rule, {
29132899
}
29142900
`,
29152901
parser: 'babel-eslint'
2902+
}, {
2903+
code: [
2904+
'import type {BasePerson} from \'./types\'',
2905+
'type Props = {',
2906+
' person: {',
2907+
' ...$Exact<BasePerson>,',
2908+
' lastname: string',
2909+
' }',
2910+
'};',
2911+
'class Hello extends React.Component {',
2912+
' props: Props;',
2913+
' render () {',
2914+
' return <div>Hello {this.props.person.firstname}</div>;',
2915+
' }',
2916+
'}'
2917+
].join('\n'),
2918+
parser: 'babel-eslint',
29162919
}
29172920
],
29182921

@@ -4475,6 +4478,48 @@ ruleTester.run('no-unused-prop-types', rule, {
44754478
errors: [{
44764479
message: '\'lastname\' PropType is defined but prop is never used'
44774480
}]
4481+
}, {
4482+
code: `
4483+
type Person = string;
4484+
class Hello extends React.Component<{ person: Person }> {
4485+
render () {
4486+
return <div />;
4487+
}
4488+
}
4489+
`,
4490+
settings: {react: {flowVersion: '0.53'}},
4491+
errors: [{
4492+
message: '\'person\' PropType is defined but prop is never used'
4493+
}],
4494+
parser: 'babel-eslint'
4495+
}, {
4496+
code: `
4497+
type Person = string;
4498+
class Hello extends React.Component<void, { person: Person }, void> {
4499+
render () {
4500+
return <div />;
4501+
}
4502+
}
4503+
`,
4504+
settings: {react: {flowVersion: '0.52'}},
4505+
errors: [{
4506+
message: '\'person\' PropType is defined but prop is never used'
4507+
}],
4508+
parser: 'babel-eslint'
4509+
}, {
4510+
code: `
4511+
function higherOrderComponent<P: { foo: string }>() {
4512+
return class extends React.Component<P> {
4513+
render() {
4514+
return <div />;
4515+
}
4516+
}
4517+
}
4518+
`,
4519+
errors: [{
4520+
message: '\'foo\' PropType is defined but prop is never used'
4521+
}],
4522+
parser: 'babel-eslint'
44784523
}, {
44794524
// issue #1506
44804525
code: [
@@ -4665,6 +4710,163 @@ ruleTester.run('no-unused-prop-types', rule, {
46654710
}, {
46664711
message: '\'a.b.c\' PropType is defined but prop is never used'
46674712
}]
4713+
}, {
4714+
code: `
4715+
type Props = { foo: string }
4716+
function higherOrderComponent<Props>() {
4717+
return class extends React.Component<Props> {
4718+
render() {
4719+
return <div />;
4720+
}
4721+
}
4722+
}
4723+
`,
4724+
parser: 'babel-eslint',
4725+
errors: [{
4726+
message: '\'foo\' PropType is defined but prop is never used'
4727+
}]
4728+
}, {
4729+
code: [
4730+
'type Person = {',
4731+
' ...data,',
4732+
' lastname: string',
4733+
'};',
4734+
'class Hello extends React.Component {',
4735+
' props: Person;',
4736+
' render () {',
4737+
' return <div>Hello {this.props.firstname}</div>;',
4738+
' }',
4739+
'}'
4740+
].join('\n'),
4741+
parser: 'babel-eslint',
4742+
errors: [{
4743+
message: '\'lastname\' PropType is defined but prop is never used'
4744+
}]
4745+
}, {
4746+
code: [
4747+
'type Person = {|',
4748+
' ...data,',
4749+
' lastname: string',
4750+
'|};',
4751+
'class Hello extends React.Component {',
4752+
' props: Person;',
4753+
' render () {',
4754+
' return <div>Hello {this.props.firstname}</div>;',
4755+
' }',
4756+
'}'
4757+
].join('\n'),
4758+
parser: 'babel-eslint',
4759+
errors: [{
4760+
message: '\'lastname\' PropType is defined but prop is never used'
4761+
}]
4762+
}, {
4763+
code: [
4764+
'type Person = {',
4765+
' ...$Exact<data>,',
4766+
' lastname: string',
4767+
'};',
4768+
'class Hello extends React.Component {',
4769+
' props: Person;',
4770+
' render () {',
4771+
' return <div>Hello {this.props.firstname}</div>;',
4772+
' }',
4773+
'}'
4774+
].join('\n'),
4775+
parser: 'babel-eslint',
4776+
errors: [{
4777+
message: '\'lastname\' PropType is defined but prop is never used'
4778+
}]
4779+
}, {
4780+
code: [
4781+
'import type {Data} from \'./Data\'',
4782+
'type Person = {',
4783+
' ...Data,',
4784+
' lastname: string',
4785+
'};',
4786+
'class Hello extends React.Component {',
4787+
' props: Person;',
4788+
' render () {',
4789+
' return <div>Hello {this.props.bar}</div>;',
4790+
' }',
4791+
'}'
4792+
].join('\n'),
4793+
parser: 'babel-eslint',
4794+
errors: [{
4795+
message: '\'lastname\' PropType is defined but prop is never used'
4796+
}]
4797+
}, {
4798+
code: [
4799+
'import type {Data} from \'some-libdef-like-flow-typed-provides\'',
4800+
'type Person = {',
4801+
' ...Data,',
4802+
' lastname: string',
4803+
'};',
4804+
'class Hello extends React.Component {',
4805+
' props: Person;',
4806+
' render () {',
4807+
' return <div>Hello {this.props.bar}</div>;',
4808+
' }',
4809+
'}'
4810+
].join('\n'),
4811+
parser: 'babel-eslint',
4812+
errors: [{
4813+
message: '\'lastname\' PropType is defined but prop is never used'
4814+
}]
4815+
}, {
4816+
code: [
4817+
'type Person = {',
4818+
' ...data,',
4819+
' lastname: string',
4820+
'};',
4821+
'class Hello extends React.Component {',
4822+
' props: Person;',
4823+
' render () {',
4824+
' return <div>Hello {this.props.firstname}</div>;',
4825+
' }',
4826+
'}'
4827+
].join('\n'),
4828+
parser: 'babel-eslint',
4829+
errors: [{
4830+
message: '\'lastname\' PropType is defined but prop is never used'
4831+
}]
4832+
}, {
4833+
code: [
4834+
'type Person = {|',
4835+
' ...data,',
4836+
' lastname: string',
4837+
'|};',
4838+
'class Hello extends React.Component {',
4839+
' props: Person;',
4840+
' render () {',
4841+
' return <div>Hello {this.props.firstname}</div>;',
4842+
' }',
4843+
'}'
4844+
].join('\n'),
4845+
parser: 'babel-eslint',
4846+
errors: [{
4847+
message: '\'lastname\' PropType is defined but prop is never used'
4848+
}]
4849+
}, {
4850+
code: [
4851+
'import type {BasePerson} from \'./types\'',
4852+
'type Props = {',
4853+
' person: {',
4854+
' ...$Exact<BasePerson>,',
4855+
' lastname: string',
4856+
' }',
4857+
'};',
4858+
'class Hello extends React.Component {',
4859+
' props: Props;',
4860+
' render () {',
4861+
' return <div>Hello {this.props.person.firstname}</div>;',
4862+
' }',
4863+
'}'
4864+
].join('\n'),
4865+
parser: 'babel-eslint',
4866+
options: [{skipShapeProps: false}],
4867+
errors: [{
4868+
message: '\'person.lastname\' PropType is defined but prop is never used'
4869+
}]
46684870
}
46694871

46704872
/* , {

0 commit comments

Comments
 (0)