From bca17b72a3ec3c7dc22848729d2e6f31cf10042b Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Tue, 16 Apr 2019 15:02:33 +0200 Subject: [PATCH] Add failing test case for refs on the root in mount Fix ref on root component pointing to wrapper Backport root ref fix to older adapters Adjust ref call to work with 0.13 host instances Use ref prop type Check if WrapperComponent supports forwardedRef prop --- .../enzyme-adapter-react-13/src/ReactThirteenAdapter.js | 7 +++++-- .../enzyme-adapter-react-14/src/ReactFourteenAdapter.js | 7 +++++-- .../src/ReactFifteenFourAdapter.js | 7 +++++-- .../enzyme-adapter-react-15/src/ReactFifteenAdapter.js | 7 +++++-- .../src/ReactSixteenOneAdapter.js | 7 +++++-- .../src/ReactSixteenTwoAdapter.js | 7 +++++-- .../src/ReactSixteenThreeAdapter.js | 7 +++++-- .../enzyme-adapter-react-16/src/ReactSixteenAdapter.js | 7 +++++-- packages/enzyme-adapter-utils/src/createMountWrapper.jsx | 7 +++++-- packages/enzyme-test-suite/test/ReactWrapper-spec.jsx | 6 ++++++ 10 files changed, 51 insertions(+), 18 deletions(-) diff --git a/packages/enzyme-adapter-react-13/src/ReactThirteenAdapter.js b/packages/enzyme-adapter-react-13/src/ReactThirteenAdapter.js index f9ba3c46f..b0d68dc70 100644 --- a/packages/enzyme-adapter-react-13/src/ReactThirteenAdapter.js +++ b/packages/enzyme-adapter-react-13/src/ReactThirteenAdapter.js @@ -125,13 +125,16 @@ class ReactThirteenAdapter extends EnzymeAdapter { render(el, context, callback) { if (instance === null) { const { ref, type, props } = el; + const ReactWrapperComponent = createMountWrapper(el, { ...options, adapter }); + const refProp = ReactWrapperComponent.supportsForwardedRef === true + ? 'forwardedRef' + : 'ref'; const wrapperProps = { Component: type, props, context, - ...(ref && { ref }), + ...(ref && { [refProp]: ref }), }; - const ReactWrapperComponent = createMountWrapper(el, { ...options, adapter }); const wrappedEl = React.createElement(ReactWrapperComponent, wrapperProps); instance = React.render(wrappedEl, domNode); if (typeof callback === 'function') { diff --git a/packages/enzyme-adapter-react-14/src/ReactFourteenAdapter.js b/packages/enzyme-adapter-react-14/src/ReactFourteenAdapter.js index 68a2d9836..48351d0e5 100644 --- a/packages/enzyme-adapter-react-14/src/ReactFourteenAdapter.js +++ b/packages/enzyme-adapter-react-14/src/ReactFourteenAdapter.js @@ -105,14 +105,17 @@ class ReactFourteenAdapter extends EnzymeAdapter { render(el, context, callback) { if (instance === null) { const { type, props, ref } = el; + const ReactWrapperComponent = createMountWrapper(el, { ...options, adapter }); + const refProp = ReactWrapperComponent.supportsForwardedRef === true + ? 'forwardedRef' + : 'ref'; const wrapperProps = { Component: type, wrappingComponentProps: options.wrappingComponentProps, props, context, - ...(ref && { refProp: ref }), + ...(ref && { [refProp]: ref }), }; - const ReactWrapperComponent = createMountWrapper(el, { ...options, adapter }); const wrappedEl = React.createElement(ReactWrapperComponent, wrapperProps); instance = ReactDOM.render(wrappedEl, domNode); if (typeof callback === 'function') { diff --git a/packages/enzyme-adapter-react-15.4/src/ReactFifteenFourAdapter.js b/packages/enzyme-adapter-react-15.4/src/ReactFifteenFourAdapter.js index 9c239d0b4..063e233ad 100644 --- a/packages/enzyme-adapter-react-15.4/src/ReactFifteenFourAdapter.js +++ b/packages/enzyme-adapter-react-15.4/src/ReactFifteenFourAdapter.js @@ -140,14 +140,17 @@ class ReactFifteenFourAdapter extends EnzymeAdapter { render(el, context, callback) { if (instance === null) { const { type, props, ref } = el; + const ReactWrapperComponent = createMountWrapper(el, { ...options, adapter }); + const refProp = ReactWrapperComponent.supportsForwardedRef === true + ? 'forwardedRef' + : 'ref'; const wrapperProps = { Component: type, wrappingComponentProps: options.wrappingComponentProps, props, context, - ...(ref && { refProp: ref }), + ...(ref && { [refProp]: ref }), }; - const ReactWrapperComponent = createMountWrapper(el, { ...options, adapter }); const wrappedEl = React.createElement(ReactWrapperComponent, wrapperProps); instance = ReactDOM.render(wrappedEl, domNode); if (typeof callback === 'function') { diff --git a/packages/enzyme-adapter-react-15/src/ReactFifteenAdapter.js b/packages/enzyme-adapter-react-15/src/ReactFifteenAdapter.js index 1cac48bec..ba349847a 100644 --- a/packages/enzyme-adapter-react-15/src/ReactFifteenAdapter.js +++ b/packages/enzyme-adapter-react-15/src/ReactFifteenAdapter.js @@ -140,14 +140,17 @@ class ReactFifteenAdapter extends EnzymeAdapter { render(el, context, callback) { if (instance === null) { const { type, props, ref } = el; + const ReactWrapperComponent = createMountWrapper(el, { ...options, adapter }); + const refProp = ReactWrapperComponent.supportsForwardedRef === true + ? 'forwardedRef' + : 'ref'; const wrapperProps = { Component: type, wrappingComponentProps: options.wrappingComponentProps, props, context, - ...(ref && { refProp: ref }), + ...(ref && { [refProp]: ref }), }; - const ReactWrapperComponent = createMountWrapper(el, { ...options, adapter }); const wrappedEl = React.createElement(ReactWrapperComponent, wrapperProps); instance = ReactDOM.render(wrappedEl, domNode); if (typeof callback === 'function') { diff --git a/packages/enzyme-adapter-react-16.1/src/ReactSixteenOneAdapter.js b/packages/enzyme-adapter-react-16.1/src/ReactSixteenOneAdapter.js index ce7ede8f0..2858727a4 100644 --- a/packages/enzyme-adapter-react-16.1/src/ReactSixteenOneAdapter.js +++ b/packages/enzyme-adapter-react-16.1/src/ReactSixteenOneAdapter.js @@ -273,14 +273,17 @@ class ReactSixteenOneAdapter extends EnzymeAdapter { render(el, context, callback) { if (instance === null) { const { type, props, ref } = el; + const ReactWrapperComponent = createMountWrapper(el, { ...options, adapter }); + const refProp = ReactWrapperComponent.supportsForwardedRef === true + ? 'forwardedRef' + : 'ref'; const wrapperProps = { Component: type, props, wrappingComponentProps, context, - ...(ref && { refProp: ref }), + ...(ref && { [refProp]: ref }), }; - const ReactWrapperComponent = createMountWrapper(el, { ...options, adapter }); const wrappedEl = React.createElement(ReactWrapperComponent, wrapperProps); instance = hydrateIn ? ReactDOM.hydrate(wrappedEl, domNode) diff --git a/packages/enzyme-adapter-react-16.2/src/ReactSixteenTwoAdapter.js b/packages/enzyme-adapter-react-16.2/src/ReactSixteenTwoAdapter.js index 1c0fe065e..db29cd173 100644 --- a/packages/enzyme-adapter-react-16.2/src/ReactSixteenTwoAdapter.js +++ b/packages/enzyme-adapter-react-16.2/src/ReactSixteenTwoAdapter.js @@ -275,14 +275,17 @@ class ReactSixteenTwoAdapter extends EnzymeAdapter { render(el, context, callback) { if (instance === null) { const { type, props, ref } = el; + const ReactWrapperComponent = createMountWrapper(el, { ...options, adapter }); + const refProp = ReactWrapperComponent.supportsForwardedRef === true + ? 'forwardedRef' + : 'ref'; const wrapperProps = { Component: type, props, wrappingComponentProps, context, - ...(ref && { refProp: ref }), + ...(ref && { [refProp]: ref }), }; - const ReactWrapperComponent = createMountWrapper(el, { ...options, adapter }); const wrappedEl = React.createElement(ReactWrapperComponent, wrapperProps); instance = hydrateIn ? ReactDOM.hydrate(wrappedEl, domNode) diff --git a/packages/enzyme-adapter-react-16.3/src/ReactSixteenThreeAdapter.js b/packages/enzyme-adapter-react-16.3/src/ReactSixteenThreeAdapter.js index fc6259fce..7e52abe18 100644 --- a/packages/enzyme-adapter-react-16.3/src/ReactSixteenThreeAdapter.js +++ b/packages/enzyme-adapter-react-16.3/src/ReactSixteenThreeAdapter.js @@ -294,14 +294,17 @@ class ReactSixteenThreeAdapter extends EnzymeAdapter { render(el, context, callback) { if (instance === null) { const { type, props, ref } = el; + const ReactWrapperComponent = createMountWrapper(el, { ...options, adapter }); + const refProp = ReactWrapperComponent.supportsForwardedRef === true + ? 'forwardedRef' + : 'ref'; const wrapperProps = { Component: type, props, wrappingComponentProps, context, - ...(ref && { refProp: ref }), + ...(ref && { [refProp]: ref }), }; - const ReactWrapperComponent = createMountWrapper(el, { ...options, adapter }); const wrappedEl = React.createElement(ReactWrapperComponent, wrapperProps); instance = hydrateIn ? ReactDOM.hydrate(wrappedEl, domNode) diff --git a/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js b/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js index eadcf94ee..041e77b78 100644 --- a/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js +++ b/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js @@ -423,14 +423,17 @@ class ReactSixteenAdapter extends EnzymeAdapter { return wrapAct(() => { if (instance === null) { const { type, props, ref } = el; + const ReactWrapperComponent = createMountWrapper(el, { ...options, adapter }); + const refProp = ReactWrapperComponent.supportsForwardedRef === true + ? 'forwardedRef' + : 'ref'; const wrapperProps = { Component: type, props, wrappingComponentProps, context, - ...(ref && { refProp: ref }), + ...(ref && { [refProp]: ref }), }; - const ReactWrapperComponent = createMountWrapper(el, { ...options, adapter }); const wrappedEl = React.createElement(ReactWrapperComponent, wrapperProps); instance = hydrateIn ? ReactDOM.hydrate(wrappedEl, domNode) diff --git a/packages/enzyme-adapter-utils/src/createMountWrapper.jsx b/packages/enzyme-adapter-utils/src/createMountWrapper.jsx index 884f0d9ef..ef3c34935 100644 --- a/packages/enzyme-adapter-utils/src/createMountWrapper.jsx +++ b/packages/enzyme-adapter-utils/src/createMountWrapper.jsx @@ -69,11 +69,11 @@ export default function createMountWrapper(node, options = {}) { } render() { - const { Component, refProp } = this.props; + const { Component, forwardedRef } = this.props; const { mount, props, wrappingComponentProps } = this.state; if (!mount) return null; // eslint-disable-next-line react/jsx-props-no-spreading - const component = ; + const component = ; if (WrappingComponent) { return ( // eslint-disable-next-line react/jsx-props-no-spreading @@ -85,17 +85,20 @@ export default function createMountWrapper(node, options = {}) { return component; } } + WrapperComponent.supportsForwardedRef = true; WrapperComponent.propTypes = { Component: makeValidElementType(adapter).isRequired, refProp: PropTypes.oneOfType([PropTypes.string, ref()]), props: PropTypes.object.isRequired, wrappingComponentProps: PropTypes.object, context: PropTypes.object, + forwardedRef: ref(), }; WrapperComponent.defaultProps = { refProp: null, context: null, wrappingComponentProps: null, + forwardedRef: undefined, }; if (options.context && (node.type.contextTypes || options.childContextTypes)) { diff --git a/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx b/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx index fb246711b..84f4c9269 100644 --- a/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx +++ b/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx @@ -107,8 +107,14 @@ describeWithDOM('mount', () => { describeWithDOM('refs', () => { it('calls ref', () => { const spy = sinon.spy(); + mount(
); + expect(spy).to.have.property('callCount', 1); + + const [instance] = spy.firstCall.args; + const element = is('<= 0.13') ? instance.getDOMNode() : instance; + expect(element).to.be.instanceOf(global.HTMLElement); }); /* global HTMLElement */