diff --git a/env.js b/env.js index e2f6e74ea..c5a0f52ad 100755 --- a/env.js +++ b/env.js @@ -56,13 +56,12 @@ const additionalDirsToRemove = [ 'node_modules/.bin/npm.cmd', ]; -const rmrfArgs = [] +const rmrfs = [] .concat(packagesToRemove) - .concat(additionalDirsToRemove) - .join(' '); + .concat(additionalDirsToRemove); Promise.resolve() - .then(() => primraf(rmrfArgs)) + .then(() => Promise.all(rmrfs.map(s => primraf(s)))) .then(() => run('npm i')) .then(() => Promise.all([ getJSON(adapterPackageJsonPath), diff --git a/packages/enzyme-adapter-react-13/package.json b/packages/enzyme-adapter-react-13/package.json index 2a814be12..cde5367e9 100644 --- a/packages/enzyme-adapter-react-13/package.json +++ b/packages/enzyme-adapter-react-13/package.json @@ -33,6 +33,7 @@ "dependencies": { "enzyme-adapter-utils": "2.9.1", "lodash": "^4.17.4", + "object.assign": "^4.0.4", "object.values": "^1.0.4", "prop-types": "^15.5.10" }, diff --git a/packages/enzyme-adapter-react-13/src/ReactThirteenAdapter.js b/packages/enzyme-adapter-react-13/src/ReactThirteenAdapter.js index 219d75505..f0f5fc41b 100644 --- a/packages/enzyme-adapter-react-13/src/ReactThirteenAdapter.js +++ b/packages/enzyme-adapter-react-13/src/ReactThirteenAdapter.js @@ -3,13 +3,14 @@ import React from 'react'; import ReactAddons from 'react/addons'; // eslint-disable-next-line import/no-unresolved, import/extensions import ReactContext from 'react/lib/ReactContext'; -import PropTypes from 'prop-types'; import values from 'object.values'; import { EnzymeAdapter } from 'enzyme'; import { propFromEvent, withSetStateAllowed, assertDomAvailable, + createRenderWrapper, + createMountWrapper, } from 'enzyme-adapter-utils'; import mapNativeEventNames from './ReactThirteenMapNativeEventNames'; import elementToTree from './ReactThirteenElementToTree'; @@ -93,31 +94,34 @@ function instanceToTree(inst) { throw new Error('Enzyme Internal Error: unknown instance encountered'); } -class SimpleWrapper extends React.Component { - render() { - return this.props.node || null; - } -} - -SimpleWrapper.propTypes = { node: PropTypes.node.isRequired }; - class ReactThirteenAdapter extends EnzymeAdapter { createMountRenderer(options) { assertDomAvailable('mount'); const domNode = options.attachTo || global.document.createElement('div'); let instance = null; return { - render(el/* , context */) { - const wrappedEl = React.createElement(SimpleWrapper, { - node: el, - }); - instance = React.render(wrappedEl, domNode); + render(el, context, callback) { + if (instance === null) { + const ReactWrapperComponent = createMountWrapper(el, options); + const wrappedEl = React.createElement(ReactWrapperComponent, { + Component: el.type, + props: el.props, + context, + }); + instance = React.render(wrappedEl, domNode); + if (typeof callback === 'function') { + callback(); + } + } else { + instance.setChildProps(el.props, context, callback); + } }, unmount() { React.unmountComponentAtNode(domNode); + instance = null; }, getNode() { - return instanceToTree(instance._reactInternalInstance._renderedComponent); + return instance ? instanceToTree(instance._reactInternalInstance).rendered : null; }, simulateEvent(node, event, mock) { const mappedEvent = mapNativeEventNames(event); @@ -186,9 +190,17 @@ class ReactThirteenAdapter extends EnzymeAdapter { }; } - createStringRenderer(/* options */) { + createStringRenderer(options) { return { - render(el /* , context */) { + render(el, context) { + if (options.context && (el.type.contextTypes || options.childContextTypes)) { + const childContextTypes = { + ...(el.type.contextTypes || {}), + ...options.childContextTypes, + }; + const ContextWrapper = createRenderWrapper(el, context, childContextTypes); + return React.renderToStaticMarkup(React.createElement(ContextWrapper)); + } return React.renderToStaticMarkup(el); }, }; diff --git a/packages/enzyme-adapter-react-14/package.json b/packages/enzyme-adapter-react-14/package.json index 1d94b0d11..911027f2d 100644 --- a/packages/enzyme-adapter-react-14/package.json +++ b/packages/enzyme-adapter-react-14/package.json @@ -33,6 +33,7 @@ "dependencies": { "enzyme-adapter-utils": "2.9.1", "lodash": "^4.17.4", + "object.assign": "^4.0.4", "object.values": "^1.0.4", "prop-types": "^15.5.10" }, diff --git a/packages/enzyme-adapter-react-14/src/ReactFourteenAdapter.js b/packages/enzyme-adapter-react-14/src/ReactFourteenAdapter.js index 5d13cb02d..8fe38f3f3 100644 --- a/packages/enzyme-adapter-react-14/src/ReactFourteenAdapter.js +++ b/packages/enzyme-adapter-react-14/src/ReactFourteenAdapter.js @@ -4,7 +4,6 @@ import ReactDOM from 'react-dom'; import ReactDOMServer from 'react-dom/server'; // eslint-disable-next-line import/no-unresolved, import/extensions import TestUtils from 'react-addons-test-utils'; -import PropTypes from 'prop-types'; import values from 'object.values'; import { EnzymeAdapter } from 'enzyme'; import { @@ -13,6 +12,8 @@ import { propFromEvent, withSetStateAllowed, assertDomAvailable, + createRenderWrapper, + createMountWrapper, } from 'enzyme-adapter-utils'; function typeToNodeType(type) { @@ -65,31 +66,34 @@ function instanceToTree(inst) { throw new Error('Enzyme Internal Error: unknown instance encountered'); } -class SimpleWrapper extends React.Component { - render() { - return this.props.node || null; - } -} - -SimpleWrapper.propTypes = { node: PropTypes.node.isRequired }; - class ReactFifteenAdapter extends EnzymeAdapter { createMountRenderer(options) { assertDomAvailable('mount'); const domNode = options.attachTo || global.document.createElement('div'); let instance = null; return { - render(el/* , context */) { - const wrappedEl = React.createElement(SimpleWrapper, { - node: el, - }); - instance = ReactDOM.render(wrappedEl, domNode); + render(el, context, callback) { + if (instance === null) { + const ReactWrapperComponent = createMountWrapper(el, options); + const wrappedEl = React.createElement(ReactWrapperComponent, { + Component: el.type, + props: el.props, + context, + }); + instance = ReactDOM.render(wrappedEl, domNode); + if (typeof callback === 'function') { + callback(); + } + } else { + instance.setChildProps(el.props, context, callback); + } }, unmount() { ReactDOM.unmountComponentAtNode(domNode); + instance = null; }, getNode() { - return instanceToTree(instance._reactInternalInstance._renderedComponent); + return instance ? instanceToTree(instance._reactInternalInstance).rendered : null; }, simulateEvent(node, event, mock) { const mappedEvent = mapNativeEventNames(event); @@ -157,9 +161,17 @@ class ReactFifteenAdapter extends EnzymeAdapter { }; } - createStringRenderer(/* options */) { + createStringRenderer(options) { return { - render(el /* , context */) { + render(el, context) { + if (options.context && (el.type.contextTypes || options.childContextTypes)) { + const childContextTypes = { + ...(el.type.contextTypes || {}), + ...options.childContextTypes, + }; + const ContextWrapper = createRenderWrapper(el, context, childContextTypes); + return ReactDOMServer.renderToStaticMarkup(React.createElement(ContextWrapper)); + } return ReactDOMServer.renderToStaticMarkup(el); }, }; diff --git a/packages/enzyme-adapter-react-15.4/package.json b/packages/enzyme-adapter-react-15.4/package.json index 6e421c6a9..1e2c70c9e 100644 --- a/packages/enzyme-adapter-react-15.4/package.json +++ b/packages/enzyme-adapter-react-15.4/package.json @@ -33,6 +33,7 @@ "dependencies": { "enzyme-adapter-utils": "2.9.1", "lodash": "^4.17.4", + "object.assign": "^4.0.4", "object.values": "^1.0.4", "prop-types": "^15.5.10" }, diff --git a/packages/enzyme-adapter-react-15.4/src/ReactFifteenFourAdapter.js b/packages/enzyme-adapter-react-15.4/src/ReactFifteenFourAdapter.js index 3c53d67fa..94b97ecd6 100644 --- a/packages/enzyme-adapter-react-15.4/src/ReactFifteenFourAdapter.js +++ b/packages/enzyme-adapter-react-15.4/src/ReactFifteenFourAdapter.js @@ -4,7 +4,6 @@ import ReactDOM from 'react-dom'; import ReactDOMServer from 'react-dom/server'; // eslint-disable-next-line import/no-unresolved, import/extensions import TestUtils from 'react-addons-test-utils'; -import PropTypes from 'prop-types'; import values from 'object.values'; import { EnzymeAdapter } from 'enzyme'; import { @@ -13,6 +12,8 @@ import { propFromEvent, withSetStateAllowed, assertDomAvailable, + createRenderWrapper, + createMountWrapper, } from 'enzyme-adapter-utils'; function compositeTypeToNodeType(type) { @@ -73,31 +74,34 @@ function instanceToTree(inst) { throw new Error('Enzyme Internal Error: unknown instance encountered'); } -class SimpleWrapper extends React.Component { - render() { - return this.props.node || null; - } -} - -SimpleWrapper.propTypes = { node: PropTypes.node.isRequired }; - class ReactFifteenFourAdapter extends EnzymeAdapter { createMountRenderer(options) { assertDomAvailable('mount'); const domNode = options.attachTo || global.document.createElement('div'); let instance = null; return { - render(el/* , context */) { - const wrappedEl = React.createElement(SimpleWrapper, { - node: el, - }); - instance = ReactDOM.render(wrappedEl, domNode); + render(el, context, callback) { + if (instance === null) { + const ReactWrapperComponent = createMountWrapper(el, options); + const wrappedEl = React.createElement(ReactWrapperComponent, { + Component: el.type, + props: el.props, + context, + }); + instance = ReactDOM.render(wrappedEl, domNode); + if (typeof callback === 'function') { + callback(); + } + } else { + instance.setChildProps(el.props, context, callback); + } }, unmount() { ReactDOM.unmountComponentAtNode(domNode); + instance = null; }, getNode() { - return instanceToTree(instance._reactInternalInstance._renderedComponent); + return instance ? instanceToTree(instance._reactInternalInstance).rendered : null; }, simulateEvent(node, event, mock) { const mappedEvent = mapNativeEventNames(event); @@ -165,9 +169,17 @@ class ReactFifteenFourAdapter extends EnzymeAdapter { }; } - createStringRenderer(/* options */) { + createStringRenderer(options) { return { - render(el /* , context */) { + render(el, context) { + if (options.context && (el.type.contextTypes || options.childContextTypes)) { + const childContextTypes = { + ...(el.type.contextTypes || {}), + ...options.childContextTypes, + }; + const ContextWrapper = createRenderWrapper(el, context, childContextTypes); + return ReactDOMServer.renderToStaticMarkup(React.createElement(ContextWrapper)); + } return ReactDOMServer.renderToStaticMarkup(el); }, }; diff --git a/packages/enzyme-adapter-react-15/package.json b/packages/enzyme-adapter-react-15/package.json index 3216e45e6..cef240649 100644 --- a/packages/enzyme-adapter-react-15/package.json +++ b/packages/enzyme-adapter-react-15/package.json @@ -33,6 +33,7 @@ "dependencies": { "enzyme-adapter-utils": "2.9.1", "lodash": "^4.17.4", + "object.assign": "^4.0.4", "object.values": "^1.0.4", "prop-types": "^15.5.10" }, diff --git a/packages/enzyme-adapter-react-15/src/ReactFifteenAdapter.js b/packages/enzyme-adapter-react-15/src/ReactFifteenAdapter.js index e608beda8..a6959a126 100644 --- a/packages/enzyme-adapter-react-15/src/ReactFifteenAdapter.js +++ b/packages/enzyme-adapter-react-15/src/ReactFifteenAdapter.js @@ -4,7 +4,6 @@ import ReactDOM from 'react-dom'; import ReactDOMServer from 'react-dom/server'; // eslint-disable-next-line import/no-unresolved, import/extensions import TestUtils from 'react-dom/test-utils'; -import PropTypes from 'prop-types'; import values from 'object.values'; import { EnzymeAdapter } from 'enzyme'; import { @@ -13,6 +12,8 @@ import { propFromEvent, withSetStateAllowed, assertDomAvailable, + createRenderWrapper, + createMountWrapper, } from 'enzyme-adapter-utils'; function compositeTypeToNodeType(type) { @@ -73,31 +74,34 @@ function instanceToTree(inst) { throw new Error('Enzyme Internal Error: unknown instance encountered'); } -class SimpleWrapper extends React.Component { - render() { - return this.props.node || null; - } -} - -SimpleWrapper.propTypes = { node: PropTypes.node.isRequired }; - class ReactFifteenAdapter extends EnzymeAdapter { createMountRenderer(options) { assertDomAvailable('mount'); const domNode = options.attachTo || global.document.createElement('div'); let instance = null; return { - render(el/* , context */) { - const wrappedEl = React.createElement(SimpleWrapper, { - node: el, - }); - instance = ReactDOM.render(wrappedEl, domNode); + render(el, context, callback) { + if (instance === null) { + const ReactWrapperComponent = createMountWrapper(el, options); + const wrappedEl = React.createElement(ReactWrapperComponent, { + Component: el.type, + props: el.props, + context, + }); + instance = ReactDOM.render(wrappedEl, domNode); + if (typeof callback === 'function') { + callback(); + } + } else { + instance.setChildProps(el.props, context, callback); + } }, unmount() { ReactDOM.unmountComponentAtNode(domNode); + instance = null; }, getNode() { - return instanceToTree(instance._reactInternalInstance._renderedComponent); + return instance ? instanceToTree(instance._reactInternalInstance).rendered : null; }, simulateEvent(node, event, mock) { const mappedEvent = mapNativeEventNames(event); @@ -165,9 +169,17 @@ class ReactFifteenAdapter extends EnzymeAdapter { }; } - createStringRenderer(/* options */) { + createStringRenderer(options) { return { - render(el /* , context */) { + render(el, context) { + if (options.context && (el.type.contextTypes || options.childContextTypes)) { + const childContextTypes = { + ...(el.type.contextTypes || {}), + ...options.childContextTypes, + }; + const ContextWrapper = createRenderWrapper(el, context, childContextTypes); + return ReactDOMServer.renderToStaticMarkup(React.createElement(ContextWrapper)); + } return ReactDOMServer.renderToStaticMarkup(el); }, }; diff --git a/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js b/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js index 9aeb18374..b2c8b2094 100644 --- a/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js +++ b/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js @@ -7,7 +7,6 @@ import ReactDOMServer from 'react-dom/server'; import ShallowRenderer from 'react-test-renderer/shallow'; // eslint-disable-next-line import/no-unresolved, import/extensions import TestUtils from 'react-dom/test-utils'; -import PropTypes from 'prop-types'; import { EnzymeAdapter } from 'enzyme'; import { elementToTree, @@ -16,6 +15,8 @@ import { propFromEvent, assertDomAvailable, withSetStateAllowed, + createRenderWrapper, + createMountWrapper, } from 'enzyme-adapter-utils'; const HostRoot = 3; @@ -143,31 +144,34 @@ function nodeToHostNode(_node) { return ReactDOM.findDOMNode(node.instance); } -class SimpleWrapper extends React.Component { - render() { - return this.props.node || null; - } -} - -SimpleWrapper.propTypes = { node: PropTypes.node.isRequired }; - class ReactSixteenAdapter extends EnzymeAdapter { createMountRenderer(options) { assertDomAvailable('mount'); const domNode = options.attachTo || global.document.createElement('div'); let instance = null; return { - render(el/* , context */) { - const wrappedEl = React.createElement(SimpleWrapper, { - node: el, - }); - instance = ReactDOM.render(wrappedEl, domNode); + render(el, context, callback) { + if (instance === null) { + const ReactWrapperComponent = createMountWrapper(el, options); + const wrappedEl = React.createElement(ReactWrapperComponent, { + Component: el.type, + props: el.props, + context, + }); + instance = ReactDOM.render(wrappedEl, domNode); + if (typeof callback === 'function') { + callback(); + } + } else { + instance.setChildProps(el.props, context, callback); + } }, unmount() { ReactDOM.unmountComponentAtNode(domNode); + instance = null; }, getNode() { - return toTree(instance._reactInternalInstance.child); + return instance ? toTree(instance._reactInternalInstance).rendered : null; }, simulateEvent(node, event, mock) { const mappedEvent = mapNativeEventNames(event); @@ -237,9 +241,17 @@ class ReactSixteenAdapter extends EnzymeAdapter { }; } - createStringRenderer(/* options */) { + createStringRenderer(options) { return { - render(el /* , context */) { + render(el, context) { + if (options.context && (el.type.contextTypes || options.childContextTypes)) { + const childContextTypes = { + ...(el.type.contextTypes || {}), + ...options.childContextTypes, + }; + const ContextWrapper = createRenderWrapper(el, context, childContextTypes); + return ReactDOMServer.renderToStaticMarkup(React.createElement(ContextWrapper)); + } return ReactDOMServer.renderToStaticMarkup(el); }, }; diff --git a/packages/enzyme-adapter-utils/package.json b/packages/enzyme-adapter-utils/package.json index 16569cfdb..7355271de 100644 --- a/packages/enzyme-adapter-utils/package.json +++ b/packages/enzyme-adapter-utils/package.json @@ -31,7 +31,12 @@ "author": "Leland Richardson ", "license": "MIT", "dependencies": { - "lodash": "^4.17.4" + "lodash": "^4.17.4", + "object.assign": "^4.0.4", + "prop-types": "^15.5.10" + }, + "peerDependencies": { + "react": "0.13.x || 0.14.x || ^15.0.0-0 || ^16.0.0-0" }, "devDependencies": { "babel-cli": "^6.24.1", diff --git a/packages/enzyme-adapter-utils/src/Utils.js b/packages/enzyme-adapter-utils/src/Utils.js index f39f14741..8a9b9873b 100644 --- a/packages/enzyme-adapter-utils/src/Utils.js +++ b/packages/enzyme-adapter-utils/src/Utils.js @@ -1,4 +1,8 @@ import flatten from 'lodash/flatten'; +import createMountWrapper from './createMountWrapper'; +import createRenderWrapper from './createRenderWrapper'; + +export { createMountWrapper, createRenderWrapper }; export function mapNativeEventNames(event) { const nativeToReactEventMap = { diff --git a/packages/enzyme/src/ReactWrapperComponent.jsx b/packages/enzyme-adapter-utils/src/createMountWrapper.jsx similarity index 91% rename from packages/enzyme/src/ReactWrapperComponent.jsx rename to packages/enzyme-adapter-utils/src/createMountWrapper.jsx index a8d298821..8ef6a4c1e 100644 --- a/packages/enzyme/src/ReactWrapperComponent.jsx +++ b/packages/enzyme-adapter-utils/src/createMountWrapper.jsx @@ -11,7 +11,7 @@ import PropTypes from 'prop-types'; * the DOM node it rendered to, so we can't really "re-render" to * pass new props in. */ -export default function createWrapperComponent(node, options = {}) { +export default function createMountWrapper(node, options = {}) { class WrapperComponent extends React.Component { constructor(...args) { super(...args); @@ -21,9 +21,10 @@ export default function createWrapperComponent(node, options = {}) { context: this.props.context, }; } - setChildProps(newProps, callback = undefined) { + setChildProps(newProps, newContext, callback = undefined) { const props = { ...this.state.props, ...newProps }; - this.setState({ props }, callback); + const context = { ...this.state.context, ...newContext }; + this.setState({ props, context }, callback); } getInstance() { const component = this._reactInternalInstance._renderedComponent; diff --git a/packages/enzyme-adapter-utils/src/createRenderWrapper.jsx b/packages/enzyme-adapter-utils/src/createRenderWrapper.jsx new file mode 100644 index 000000000..dfedbcb77 --- /dev/null +++ b/packages/enzyme-adapter-utils/src/createRenderWrapper.jsx @@ -0,0 +1,14 @@ +import React from 'react'; + +export default function createRenderWrapper(node, context, childContextTypes) { + class ContextWrapper extends React.Component { + getChildContext() { + return context; + } + render() { + return node; + } + } + ContextWrapper.childContextTypes = childContextTypes; + return ContextWrapper; +} diff --git a/packages/enzyme/package.json b/packages/enzyme/package.json index 901e35994..f6a5cba88 100644 --- a/packages/enzyme/package.json +++ b/packages/enzyme/package.json @@ -38,13 +38,9 @@ "object-is": "^1.0.1", "object.assign": "^4.0.4", "object.entries": "^1.0.4", - "prop-types": "^15.5.10", "raf": "^3.3.2", "uuid": "^3.1.0" }, - "peerDependencies": { - "react": "0.13.x || 0.14.x || ^15.0.0-0 || ^16.0.0-0" - }, "devDependencies": { "babel-cli": "^6.24.1", "eslint": "^4.4.1", diff --git a/packages/enzyme/src/ReactWrapper.jsx b/packages/enzyme/src/ReactWrapper.js similarity index 96% rename from packages/enzyme/src/ReactWrapper.jsx rename to packages/enzyme/src/ReactWrapper.js index 8cbc4a3d6..fd2c18da6 100644 --- a/packages/enzyme/src/ReactWrapper.jsx +++ b/packages/enzyme/src/ReactWrapper.js @@ -1,11 +1,9 @@ -import React from 'react'; import cheerio from 'cheerio'; import flatten from 'lodash/flatten'; import unique from 'lodash/uniq'; import compact from 'lodash/compact'; import ComplexSelector from './ComplexSelector'; -import createWrapperComponent from './ReactWrapperComponent'; import { containsChildrenSubArray, typeOfNode, @@ -16,6 +14,7 @@ import { getAdapter, sym, privateSet, + cloneElement, } from './Utils'; import { debugNodes, @@ -33,8 +32,8 @@ const noop = () => {}; const NODE = sym('__node__'); const NODES = sym('__nodes__'); -const COMPONENT = sym('__component__'); const RENDERER = sym('__renderer__'); +const UNRENDERED = sym('__unrendered__'); const ROOT = sym('__root__'); const OPTIONS = sym('__options__'); const COMPLEX_SELECTOR = sym('__complexSelector__'); @@ -64,14 +63,6 @@ function filterWhereUnwrapped(wrapper, predicate) { return wrapper.wrap(compact(wrapper.getNodesInternal().filter(predicate))); } -function getFromRenderer(renderer) { - const root = renderer.getNode(); - return { - component: root.instance, - node: root.rendered, - }; -} - /** * @class ReactWrapper */ @@ -84,26 +75,17 @@ class ReactWrapper { } if (!root) { + privateSet(this, UNRENDERED, nodes); const renderer = getAdapter(options).createRenderer({ mode: 'mount', ...options }); privateSet(this, RENDERER, renderer); - const ReactWrapperComponent = createWrapperComponent(nodes, options); - renderer.render( - , - ); + renderer.render(nodes, options.context); privateSet(this, ROOT, this); - const { - component, - node, - } = getFromRenderer(this[RENDERER]); - privateSet(this, COMPONENT, component); + const node = this[RENDERER].getNode(); privateSet(this, NODE, node); privateSet(this, NODES, [node]); this.length = 1; } else { + privateSet(this, UNRENDERED, null); privateSet(this, RENDERER, root[RENDERER]); privateSet(this, ROOT, root); if (!nodes) { @@ -117,7 +99,7 @@ class ReactWrapper { privateSet(this, NODES, nodes); } this.length = this[NODES].length; - privateSet(this, COMPONENT, null); + // privateSet(this, COMPONENT, null); } privateSet(this, OPTIONS, root ? root[OPTIONS] : options); privateSet(this, COMPLEX_SELECTOR, new ComplexSelector( @@ -248,11 +230,7 @@ class ReactWrapper { throw new Error('ReactWrapper::update() can only be called on the root'); } this.single('update', () => { - const { - component, - node, - } = getFromRenderer(this[RENDERER]); - this[COMPONENT] = component; + const node = this[RENDERER].getNode(); this[NODE] = node; this[NODES] = [node]; }); @@ -270,7 +248,7 @@ class ReactWrapper { throw new Error('ReactWrapper::unmount() can only be called on the root'); } this.single('unmount', () => { - this[COMPONENT].setState({ mount: false }); + this[RENDERER].unmount(); this.update(); }); return this; @@ -287,8 +265,7 @@ class ReactWrapper { throw new Error('ReactWrapper::mount() can only be called on the root'); } this.single('mount', () => { - this[COMPONENT].setState({ mount: true }); - this.update(); + this[RENDERER].render(this[UNRENDERED], this[OPTIONS].context, () => this.update()); }); return this; } @@ -314,7 +291,9 @@ class ReactWrapper { if (typeof callback !== 'function') { throw new TypeError('ReactWrapper::setProps() expects a function as its second argument'); } - this[COMPONENT].setChildProps(props, () => { + const adapter = getAdapter(this[OPTIONS]); + this[UNRENDERED] = cloneElement(adapter, this[UNRENDERED], props); + this[RENDERER].render(this[UNRENDERED], null, () => { this.update(); callback(); }); @@ -367,8 +346,7 @@ class ReactWrapper { 'a context option', ); } - this[COMPONENT].setChildContext(context); - this.update(); + this[RENDERER].render(this[UNRENDERED], context, () => this.update()); return this; } diff --git a/packages/enzyme/src/ShallowWrapper.js b/packages/enzyme/src/ShallowWrapper.js index 3044b1cde..c4defa1bc 100644 --- a/packages/enzyme/src/ShallowWrapper.js +++ b/packages/enzyme/src/ShallowWrapper.js @@ -1,4 +1,3 @@ -import React from 'react'; import flatten from 'lodash/flatten'; import unique from 'lodash/uniq'; import compact from 'lodash/compact'; @@ -18,6 +17,7 @@ import { getAdapter, sym, privateSet, + cloneElement, } from './Utils'; import { debugNodes, @@ -252,6 +252,7 @@ class ShallowWrapper { * @returns {ShallowWrapper} */ rerender(props, context) { + const adapter = getAdapter(this[OPTIONS]); this.single('rerender', () => { withSetStateAllowed(() => { // NOTE(lmr): In react 16, instances will be null for SFCs, but @@ -291,7 +292,7 @@ class ShallowWrapper { originalShouldComponentUpdate = instance.shouldComponentUpdate; } if (shouldRender) { - if (props) this[UNRENDERED] = React.cloneElement(this[UNRENDERED], props); + if (props) this[UNRENDERED] = cloneElement(adapter, this[UNRENDERED], props); if (originalShouldComponentUpdate) { instance.shouldComponentUpdate = () => true; } diff --git a/packages/enzyme/src/Utils.js b/packages/enzyme/src/Utils.js index b021d812c..12f36de51 100644 --- a/packages/enzyme/src/Utils.js +++ b/packages/enzyme/src/Utils.js @@ -374,3 +374,10 @@ export function privateSet(obj, prop, value) { writable: true, }); } + +export function cloneElement(adapter, el, props) { + return adapter.createElement( + el.type, + { ...el.props, ...props }, + ); +} diff --git a/packages/enzyme/src/render.jsx b/packages/enzyme/src/render.js similarity index 53% rename from packages/enzyme/src/render.jsx rename to packages/enzyme/src/render.js index cd9bc0438..d24f4ebf0 100644 --- a/packages/enzyme/src/render.jsx +++ b/packages/enzyme/src/render.js @@ -1,4 +1,3 @@ -import React from 'react'; import cheerio from 'cheerio'; import { getAdapter } from './Utils'; @@ -16,31 +15,9 @@ import { getAdapter } from './Utils'; * @returns {Cheerio} */ -function createContextWrapperForNode(node, context, childContextTypes) { - class ContextWrapper extends React.Component { - getChildContext() { - return context; - } - render() { - return node; - } - } - ContextWrapper.childContextTypes = childContextTypes; - return ContextWrapper; -} - export default function render(node, options = {}) { const adapter = getAdapter(options); const renderer = adapter.createRenderer({ mode: 'string', ...options }); - if (options.context && (node.type.contextTypes || options.childContextTypes)) { - const childContextTypes = { - ...(node.type.contextTypes || {}), - ...options.childContextTypes, - }; - const ContextWrapper = createContextWrapperForNode(node, options.context, childContextTypes); - const html = renderer.render(); - return cheerio.load(html).root(); - } - const html = renderer.render(node); + const html = renderer.render(node, options.context); return cheerio.load(html).root(); }