Skip to content

Commit 7429b19

Browse files
Merge pull request #130 from airbnb/lmr--fix-prop-selector-string-issue
Fixed confusing prop selector behavior. Added helpful warning
2 parents 3feb0ca + 0033fbf commit 7429b19

File tree

6 files changed

+64
-15
lines changed

6 files changed

+64
-15
lines changed

src/MountedTraversal.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export function instHasProperty(inst, propKey, stringifiedPropValue) {
7777
const nodeProps = propsOfNode(node);
7878
const nodePropValue = nodeProps[propKey];
7979

80-
const propValue = coercePropValue(stringifiedPropValue);
80+
const propValue = coercePropValue(propKey, stringifiedPropValue);
8181

8282
// intentionally not matching node props that are undefined
8383
if (nodePropValue === undefined) {

src/ShallowTraversal.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export function nodeHasId(node, id) {
7575

7676
export function nodeHasProperty(node, propKey, stringifiedPropValue) {
7777
const nodeProps = propsOfNode(node);
78-
const propValue = coercePropValue(stringifiedPropValue);
78+
const propValue = coercePropValue(propKey, stringifiedPropValue);
7979
const nodePropValue = nodeProps[propKey];
8080

8181
if (nodePropValue === undefined) {

src/Utils.js

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -149,27 +149,36 @@ export function AND(fns) {
149149
};
150150
}
151151

152-
export function coercePropValue(propValue) {
152+
export function coercePropValue(propName, propValue) {
153153
// can be undefined
154154
if (propValue === undefined) {
155155
return propValue;
156156
}
157157

158+
const trimmedValue = propValue.trim();
159+
158160
// if propValue includes quotes, it should be
159161
// treated as a string
160-
if (propValue.search(/"/) !== -1) {
161-
return propValue.replace(/"/g, '');
162+
if (/^(['"]).*\1$/.test(trimmedValue)) {
163+
return trimmedValue.slice(1, -1);
162164
}
163165

164-
const numericPropValue = parseInt(propValue, 10);
166+
const numericPropValue = +trimmedValue;
165167

166168
// if parseInt is not NaN, then we've wanted a number
167169
if (!isNaN(numericPropValue)) {
168170
return numericPropValue;
169171
}
170172

171173
// coerce to boolean
172-
return propValue === 'true' ? true : false;
174+
if (trimmedValue === 'true') return true;
175+
if (trimmedValue === 'false') return false;
176+
177+
// user provided an unquoted string value
178+
throw new TypeError(
179+
`Enzyme::Unable to parse selector '[${propName}=${propValue}]'. ` +
180+
`Perhaps you forgot to escape a string? Try '[${propName}="${trimmedValue}"]' instead.`
181+
);
173182
}
174183

175184
export function mapNativeEventNames(event) {

src/__tests__/ShallowTraversal-spec.js

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,13 @@ describe('ShallowTraversal', () => {
6464
const node = (<div onChange={noop} title="foo" />);
6565

6666
expect(nodeHasProperty(node, 'onChange')).to.equal(true);
67-
expect(nodeHasProperty(node, 'title', 'foo')).to.equal(true);
67+
expect(nodeHasProperty(node, 'title', '"foo"')).to.equal(true);
6868
});
6969

7070
it('should not match on html attributes', () => {
7171
const node = (<div htmlFor="foo" />);
7272

73-
expect(nodeHasProperty(node, 'for', 'foo')).to.equal(false);
73+
expect(nodeHasProperty(node, 'for', '"foo"')).to.equal(false);
7474
});
7575

7676
it('should not find undefined properties', () => {
@@ -79,6 +79,35 @@ describe('ShallowTraversal', () => {
7979
expect(nodeHasProperty(node, 'title')).to.equal(false);
8080
});
8181

82+
it('should parse false as a literal', () => {
83+
const node = (<div foo={false} />);
84+
85+
expect(nodeHasProperty(node, 'foo', 'false')).to.equal(true);
86+
});
87+
88+
it('should parse false as a literal', () => {
89+
const node = (<div foo />);
90+
91+
expect(nodeHasProperty(node, 'foo', 'true')).to.equal(true);
92+
});
93+
94+
it('should parse numbers as numeric literals', () => {
95+
expect(nodeHasProperty(<div foo={2.3} />, 'foo', '2.3')).to.equal(true);
96+
expect(nodeHasProperty(<div foo={2} />, 'foo', '2')).to.equal(true);
97+
expect(() => nodeHasProperty(<div foo={2} />, 'foo', '2abc')).to.throw();
98+
expect(() => nodeHasProperty(<div foo={2} />, 'foo', 'abc2')).to.throw();
99+
expect(nodeHasProperty(<div foo={-2} />, 'foo', '-2')).to.equal(true);
100+
expect(nodeHasProperty(<div foo={2e8} />, 'foo', '2e8')).to.equal(true);
101+
expect(nodeHasProperty(<div foo={Infinity} />, 'foo', 'Infinity')).to.equal(true);
102+
expect(nodeHasProperty(<div foo={-Infinity} />, 'foo', '-Infinity')).to.equal(true);
103+
});
104+
105+
it('should throw when un unquoted string is passed in', () => {
106+
const node = (<div title="foo" />);
107+
108+
expect(() => nodeHasProperty(node, 'title', 'foo')).to.throw();
109+
});
110+
82111
});
83112

84113
describe('treeForEach', () => {

src/__tests__/ShallowWrapper-spec.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,17 @@ describe('shallow', () => {
169169
expect(wrapper.find('[title]')).to.have.length(1);
170170
});
171171

172+
it('should error sensibly if prop selector without quotes', () => {
173+
const wrapper = shallow(
174+
<div>
175+
<input type="text" />
176+
<input type="hidden" />
177+
</div>
178+
);
179+
180+
expect(() => wrapper.find('[type=text]')).to.throw();
181+
});
182+
172183
it('should compound tag and prop selector', () => {
173184
const wrapper = shallow(
174185
<div>

src/__tests__/Utils-spec.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -227,19 +227,19 @@ describe('Utils', () => {
227227
});
228228

229229
describe('coercePropValue', () => {
230-
230+
const key = 'foo';
231231
it('returns undefined if passed undefined', () => {
232-
expect(coercePropValue(undefined)).to.equal(undefined);
232+
expect(coercePropValue(key, undefined)).to.equal(undefined);
233233
});
234234

235235
it('returns number if passed a stringified number', () => {
236-
expect(coercePropValue('1')).to.be.equal(1);
237-
expect(coercePropValue('0')).to.be.equal(0);
236+
expect(coercePropValue(key, '1')).to.be.equal(1);
237+
expect(coercePropValue(key, '0')).to.be.equal(0);
238238
});
239239

240240
it('returns a boolean if passed a stringified bool', () => {
241-
expect(coercePropValue('true')).to.equal(true);
242-
expect(coercePropValue('false')).to.equal(false);
241+
expect(coercePropValue(key, 'true')).to.equal(true);
242+
expect(coercePropValue(key, 'false')).to.equal(false);
243243
});
244244
});
245245

0 commit comments

Comments
 (0)