Skip to content

Commit 64051d9

Browse files
authored
Merge pull request #1721 from emuraton/setProps-fix-test
[New] `shallow`: `setProps`: add callback argument
2 parents e6287cc + 34d69ce commit 64051d9

File tree

3 files changed

+178
-4
lines changed

3 files changed

+178
-4
lines changed

packages/enzyme-test-suite/test/ReactWrapper-spec.jsx

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1353,7 +1353,7 @@ describeWithDOM('mount', () => {
13531353
]);
13541354
});
13551355

1356-
itIf(!REACT16, 'should throw if an exception occurs during render', () => {
1356+
it('should throw if an exception occurs during render', () => {
13571357
class Trainwreck extends React.Component {
13581358
render() {
13591359
const { user } = this.props;
@@ -1413,6 +1413,62 @@ describeWithDOM('mount', () => {
14131413
});
14141414
});
14151415

1416+
it('should call componentWillReceiveProps, shouldComponentUpdate, componentWillUpdate, and componentDidUpdate with merged newProps', () => {
1417+
const spy = sinon.spy();
1418+
1419+
class Foo extends React.Component {
1420+
componentWillReceiveProps(nextProps) {
1421+
spy('componentWillReceiveProps', this.props, nextProps);
1422+
}
1423+
1424+
shouldComponentUpdate(nextProps) {
1425+
spy('shouldComponentUpdate', this.props, nextProps);
1426+
return true;
1427+
}
1428+
1429+
componentWillUpdate(nextProps) {
1430+
spy('componentWillUpdate', this.props, nextProps);
1431+
}
1432+
1433+
componentDidUpdate(prevProps) {
1434+
spy('componentDidUpdate', prevProps, this.props);
1435+
}
1436+
1437+
render() {
1438+
return (
1439+
<div />
1440+
);
1441+
}
1442+
}
1443+
1444+
const wrapper = mount(<Foo a="a" b="b" />);
1445+
1446+
wrapper.setProps({ b: 'c', d: 'e' });
1447+
1448+
expect(spy.args).to.deep.equal([
1449+
[
1450+
'componentWillReceiveProps',
1451+
{ a: 'a', b: 'b' },
1452+
{ a: 'a', b: 'c', d: 'e' },
1453+
],
1454+
[
1455+
'shouldComponentUpdate',
1456+
{ a: 'a', b: 'b' },
1457+
{ a: 'a', b: 'c', d: 'e' },
1458+
],
1459+
[
1460+
'componentWillUpdate',
1461+
{ a: 'a', b: 'b' },
1462+
{ a: 'a', b: 'c', d: 'e' },
1463+
],
1464+
[
1465+
'componentDidUpdate',
1466+
{ a: 'a', b: 'b' },
1467+
{ a: 'a', b: 'c', d: 'e' },
1468+
],
1469+
]);
1470+
});
1471+
14161472
describeIf(is('> 0.13'), 'stateless function components', () => {
14171473
it('should set props for a component multiple times', () => {
14181474
const Foo = props => (
@@ -1445,7 +1501,21 @@ describeWithDOM('mount', () => {
14451501
expect(wrapper.props().d).to.equal('e');
14461502
});
14471503

1448-
itIf(!REACT16, 'should throw if an exception occurs during render', () => {
1504+
it('should pass in old context', () => {
1505+
const Foo = (props, context) => (
1506+
<div>{context.x}</div>
1507+
);
1508+
Foo.contextTypes = { x: PropTypes.string };
1509+
1510+
const context = { x: 'yolo' };
1511+
const wrapper = mount(<Foo x={5} />, { context });
1512+
expect(wrapper.first('div').text()).to.equal('yolo');
1513+
1514+
wrapper.setProps({ x: 5 }); // Just force a re-render
1515+
expect(wrapper.first('div').text()).to.equal('yolo');
1516+
});
1517+
1518+
it('should throw if an exception occurs during render', () => {
14491519
const Trainwreck = ({ user }) => (
14501520
<div>
14511521
{user.name.givenName}

packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1240,6 +1240,66 @@ describe('shallow', () => {
12401240
]);
12411241
});
12421242

1243+
it('should throw if an exception occurs during render', () => {
1244+
class Trainwreck extends React.Component {
1245+
render() {
1246+
const { user } = this.props;
1247+
return (
1248+
<div>
1249+
{user.name.givenName}
1250+
</div>
1251+
);
1252+
}
1253+
}
1254+
1255+
const similarException = ((() => {
1256+
const user = {};
1257+
try {
1258+
return user.name.givenName;
1259+
} catch (e) {
1260+
return e;
1261+
}
1262+
})());
1263+
1264+
const validUser = {
1265+
name: {
1266+
givenName: 'Brian',
1267+
},
1268+
};
1269+
1270+
const wrapper = shallow(<Trainwreck user={validUser} />);
1271+
1272+
const setInvalidProps = () => {
1273+
wrapper.setProps({
1274+
user: {},
1275+
});
1276+
};
1277+
1278+
expect(setInvalidProps).to.throw(TypeError, similarException.message);
1279+
});
1280+
1281+
it('should call the callback when setProps has completed', () => {
1282+
class Foo extends React.Component {
1283+
render() {
1284+
const { id } = this.props;
1285+
return (
1286+
<div className={id}>
1287+
{id}
1288+
</div>
1289+
);
1290+
}
1291+
}
1292+
const wrapper = shallow(<Foo id="foo" />);
1293+
expect(wrapper.find('.foo')).to.have.lengthOf(1);
1294+
1295+
wrapper[sym('__renderer__')].batchedUpdates(() => {
1296+
wrapper.setProps({ id: 'bar', foo: 'bla' }, () => {
1297+
expect(wrapper.find('.bar')).to.have.lengthOf(1);
1298+
});
1299+
});
1300+
expect(wrapper.find('.foo')).to.have.lengthOf(0);
1301+
});
1302+
12431303
it('should call componentWillReceiveProps, shouldComponentUpdate, componentWillUpdate, and componentDidUpdate with merged newProps', () => {
12441304
const spy = sinon.spy();
12451305

@@ -1341,6 +1401,42 @@ describe('shallow', () => {
13411401
wrapper.setProps({ x: 5 }); // Just force a re-render
13421402
expect(wrapper.first('div').text()).to.equal('yolo');
13431403
});
1404+
1405+
it('should throw if an exception occurs during render', () => {
1406+
const Trainwreck = ({ user }) => (
1407+
<div>
1408+
{user.name.givenName}
1409+
</div>
1410+
);
1411+
1412+
const validUser = {
1413+
name: {
1414+
givenName: 'Brian',
1415+
},
1416+
};
1417+
1418+
const similarException = ((() => {
1419+
const user = {};
1420+
try {
1421+
return user.name.givenName;
1422+
} catch (e) {
1423+
return e;
1424+
}
1425+
})());
1426+
1427+
const wrapper = shallow(<Trainwreck user={validUser} />);
1428+
1429+
const setInvalidProps = () => {
1430+
wrapper.setProps({
1431+
user: {},
1432+
});
1433+
};
1434+
1435+
expect(setInvalidProps).to.throw(
1436+
TypeError,
1437+
similarException.message,
1438+
);
1439+
});
13441440
});
13451441
});
13461442

packages/enzyme/src/ShallowWrapper.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import {
3030
} from './RSTTraversal';
3131
import { buildPredicate, reduceTreesBySelector } from './selectors';
3232

33+
const noop = () => {};
34+
3335
const NODE = sym('__node__');
3436
const NODES = sym('__nodes__');
3537
const RENDERER = sym('__renderer__');
@@ -376,13 +378,19 @@ class ShallowWrapper {
376378
* NOTE: can only be called on a wrapper instance that is also the root instance.
377379
*
378380
* @param {Object} props object
381+
* @param {Function} cb - callback function
379382
* @returns {ShallowWrapper}
380383
*/
381-
setProps(props) {
384+
setProps(props, callback = noop) {
382385
if (this[ROOT] !== this) {
383386
throw new Error('ShallowWrapper::setProps() can only be called on the root');
384387
}
385-
return this.rerender(props);
388+
if (typeof callback !== 'function') {
389+
throw new TypeError('ShallowWrapper::setProps() expects a function as its second argument');
390+
}
391+
this.rerender(props);
392+
callback();
393+
return this;
386394
}
387395

388396
/**

0 commit comments

Comments
 (0)