diff --git a/SUMMARY.md b/SUMMARY.md index b2adcb25a..3fedeea45 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -48,6 +48,7 @@ * [hostNodes()](/docs/api/ShallowWrapper/hostNodes.md) * [html()](/docs/api/ShallowWrapper/html.md) * [instance()](/docs/api/ShallowWrapper/instance.md) + * [invoke(propName)](/docs/api/ShallowWrapper/invoke.md) * [is(selector)](/docs/api/ShallowWrapper/is.md) * [isEmpty()](/docs/api/ShallowWrapper/isEmpty.md) * [isEmptyRender()](/docs/api/ShallowWrapper/isEmptyRender.md) @@ -108,6 +109,7 @@ * [hostNodes()](/docs/api/ReactWrapper/hostNodes.md) * [html()](/docs/api/ReactWrapper/html.md) * [instance()](/docs/api/ReactWrapper/instance.md) + * [invoke(propName)](/docs/api/ReactWrapper/invoke.md) * [is(selector)](/docs/api/ReactWrapper/is.md) * [isEmpty()](/docs/api/ReactWrapper/isEmpty.md) * [isEmptyRender()](/docs/api/ReactWrapper/isEmptyRender.md) diff --git a/docs/api/ReactWrapper/invoke.md b/docs/api/ReactWrapper/invoke.md new file mode 100644 index 000000000..48a4171a4 --- /dev/null +++ b/docs/api/ReactWrapper/invoke.md @@ -0,0 +1,40 @@ +# `.invoke(propName)(...args]) => Any` + +Invokes a function prop. + +#### Arguments + +1. `propName` (`String`): The function prop that is invoked +2. `...args` (`Any` [optional]): Arguments that is passed to the prop function + + + +#### Returns + +`Any`: Returns the value from the prop function + +#### Example + +```jsx +class Foo extends React.Component { + loadData() { + return fetch(); + } + render() { + return ( +
+ +
+ ); + } +} +const wrapper = mount(); +wrapper.find('a').invoke('onClick')().then(() => { + // expect() +}); +``` diff --git a/docs/api/ShallowWrapper/invoke.md b/docs/api/ShallowWrapper/invoke.md new file mode 100644 index 000000000..caff94fcb --- /dev/null +++ b/docs/api/ShallowWrapper/invoke.md @@ -0,0 +1,40 @@ +# `.invoke(invokePropName)(...args]) => Any` + +Invokes a function prop. + +#### Arguments + +1. `propName` (`String`): The function prop that is invoked +2. `...args` (`Any` [optional]): Arguments that is passed to the prop function + +This essentially calls wrapper.prop(propName)(...args). + +#### Returns + +`Any`: Returns the value from the prop function + +#### Example + +```jsx +class Foo extends React.Component { + loadData() { + return fetch(); + } + render() { + return ( +
+ +
+ ); + } +} +const wrapper = shallow(); +wrapper.find('a').invoke('onClick')().then(() => { + // expect() +}); +``` diff --git a/docs/api/mount.md b/docs/api/mount.md index 58b4e1f16..db2b6e8cd 100644 --- a/docs/api/mount.md +++ b/docs/api/mount.md @@ -159,6 +159,9 @@ Returns the props of the root component. #### [`.prop(key) => Any`](ReactWrapper/prop.md) Returns the named prop of the root component. +#### [`.invoke(propName)(...args) => Any`](ReactWrapper/invoke.md) +Invokes a prop function on the current node and returns the function's return value. + #### [`.key() => String`](ReactWrapper/key.md) Returns the key of the root component. diff --git a/docs/api/shallow.md b/docs/api/shallow.md index 7fd47d65a..3b67977ad 100644 --- a/docs/api/shallow.md +++ b/docs/api/shallow.md @@ -175,6 +175,9 @@ Returns the named prop of the current node. #### [`.key() => String`](ShallowWrapper/key.md) Returns the key of the current node. +#### [`.invoke(propName)(...args) => Any`](ShallowWrapper/invoke.md) +Invokes a prop function on the current node and returns the function's return value. + #### [`.simulate(event[, data]) => ShallowWrapper`](ShallowWrapper/simulate.md) Simulates an event on the current node. diff --git a/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx b/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx index e39eaa063..451abe8a1 100644 --- a/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx +++ b/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx @@ -5408,6 +5408,70 @@ describeWithDOM('mount', () => { }); }); + describe('.invoke(propName)(..args)', () => { + it('can update the state value', () => { + class Foo extends React.Component { + constructor(props) { + super(props); + this.state = { + count: 0, + }; + } + + render() { + const { count } = this.state; + return ( +
+ +
+ ); + } + } + const wrapper = mount(); + wrapper.find('button').invoke('onClick')(); + expect(wrapper.state('count')).to.equal(1); + }); + it('can return the handlers return value', () => { + const spy = sinon.stub().returns(123); + class Foo extends React.Component { + render() { + return ( +
+ foo +
+ ); + } + } + const wrapper = mount(); + const value = wrapper.find('a').invoke('onClick')(); + expect(value).to.equal(123); + expect(spy.called).to.equal(true); + }); + it('can pass in arguments', () => { + const spy = sinon.spy(); + class Foo extends React.Component { + render() { + return ( +
+ foo +
+ ); + } + } + const wrapper = mount(); + const a = {}; + const b = {}; + wrapper.find('a').invoke('onClick')(a, b); + expect(spy.args[0][0]).to.equal(a); + expect(spy.args[0][1]).to.equal(b); + }); + }); + wrap() .withConsoleThrows() .describe('.renderProp()', () => { diff --git a/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx b/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx index 422ee07f4..6a85801e3 100644 --- a/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx +++ b/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx @@ -5489,6 +5489,70 @@ describe('shallow', () => { }); }); + describe('.invoke(propName)(..args)', () => { + it('can update the state value', () => { + class Foo extends React.Component { + constructor(props) { + super(props); + this.state = { + count: 0, + }; + } + + render() { + const { count } = this.state; + return ( +
+ +
+ ); + } + } + const wrapper = shallow(); + wrapper.find('button').invoke('onClick')(); + expect(wrapper.state('count')).to.equal(1); + }); + it('can return the handlers return value', () => { + const spy = sinon.stub().returns(123); + class Foo extends React.Component { + render() { + return ( +
+ +
+ ); + } + } + const wrapper = shallow(); + const value = wrapper.find('button').invoke('onClick')(); + expect(value).to.equal(123); + expect(spy.called).to.equal(true); + }); + it('can pass in arguments', () => { + const spy = sinon.spy(); + class Foo extends React.Component { + render() { + return ( +
+ +
+ ); + } + } + const wrapper = shallow(); + const a = {}; + const b = {}; + wrapper.find('button').invoke('onClick')(a, b); + expect(spy.args[0][0]).to.equal(a); + expect(spy.args[0][1]).to.equal(b); + }); + }); + wrap() .withConsoleThrows() .describe('.renderProp()', () => { diff --git a/packages/enzyme/src/ReactWrapper.js b/packages/enzyme/src/ReactWrapper.js index 6b6c9e3f0..5de937047 100644 --- a/packages/enzyme/src/ReactWrapper.js +++ b/packages/enzyme/src/ReactWrapper.js @@ -802,6 +802,27 @@ class ReactWrapper { return this.props()[propName]; } + /** + * Used to invoke a function prop. + * Will invoke an function prop and return its value. + * + * @param {String} propName + * @returns {Any} + */ + invoke(propName) { + return this.single('invoke', () => { + const handler = this.prop(propName); + if (typeof handler !== 'function') { + throw new TypeError('ReactWrapper::invoke() expects a function prop name as its first argument'); + } + return (...args) => { + const response = handler.apply(this, args); + this[ROOT].update(); + return response; + }; + }); + } + /** * Returns a wrapper of the node rendered by the provided render prop. * diff --git a/packages/enzyme/src/ShallowWrapper.js b/packages/enzyme/src/ShallowWrapper.js index b0165110e..b7d4f6cf1 100644 --- a/packages/enzyme/src/ShallowWrapper.js +++ b/packages/enzyme/src/ShallowWrapper.js @@ -1132,6 +1132,27 @@ class ShallowWrapper { return this.props()[propName]; } + /** + * Used to invoke a function prop. + * Will invoke an function prop and return its value. + * + * @param {String} propName + * @returns {Any} + */ + invoke(propName) { + return this.single('invoke', () => { + const handler = this.prop(propName); + if (typeof handler !== 'function') { + throw new TypeError('ShallowWrapper::invoke() expects a function prop name as its first argument'); + } + return (...args) => { + const response = handler.apply(this, args); + this[ROOT].update(); + return response; + }; + }); + } + /** * Returns a wrapper of the node rendered by the provided render prop. *