diff --git a/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx b/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx
index c7e99a08f..a289c0fe3 100644
--- a/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx
+++ b/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx
@@ -239,6 +239,156 @@ describe('shallow', () => {
expect(wrapper.find('.child2')).to.have.lengthOf(1);
});
+ describeIf(is('>= 16.3'), 'rendering as root:', () => {
+ let Context;
+
+ beforeEach(() => {
+ Context = createContext('cool');
+ });
+
+ describe('', () => {
+ it('can be rendered as the root', () => {
+ const wrapper = shallow(
+
+
+ {value => {value}
}
+
+ ,
+ );
+ expect(wrapper.debug()).to.eql(`
+
+ [function]
+
+ `.trim());
+ });
+
+ it('supports changing the value', () => {
+ const wrapper = shallow(
+
+
+ {value => {value}
}
+
+ ,
+ );
+ wrapper.setProps({ value: 'world' });
+ expect(wrapper.find(Context.Consumer).dive().text()).to.eql('world');
+ });
+ });
+
+ describe('', () => {
+ function DivRenderer({ children }) {
+ return
{children}
;
+ }
+ it('can be rendered as the root', () => {
+ const wrapper = shallow(
+
+ {value => {value}}
+ ,
+ );
+ expect(wrapper.debug()).to.eql(`
+
+ cool
+
+ `.trim());
+ });
+
+ it('supports changing the children', () => {
+ const wrapper = shallow(
+
+ {value => {value}}
+ ,
+ );
+ wrapper.setProps({ children: value => Changed: {value} });
+ expect(wrapper.find(DivRenderer).dive().text()).to.eql('Changed: cool');
+ });
+ });
+ });
+
+ describeIf(is('>= 16.3'), 'dive() on Provider and Consumer', () => {
+ let Provider;
+ let Consumer;
+
+ beforeEach(() => {
+ ({ Provider, Consumer } = React.createContext('howdy!'));
+ });
+
+ class Consumes extends React.Component {
+ render() {
+ return (
+
+ {value => {value}}
+
+ );
+ }
+ }
+
+ class Provides extends React.Component {
+ render() {
+ const { children } = this.props;
+
+ return (
+
+ );
+ }
+ }
+
+ class MyComponent extends React.Component {
+ render() {
+ return (
+
+ );
+ }
+ }
+
+ it('works on a Provider', () => {
+ expect(shallow()
+ .find(Provides)
+ .dive()
+ .find(Provider)
+ .dive()
+ .text()).to.equal('');
+ });
+
+ it('always gives the default provider value if dive()ing directly to a ', () => {
+ // Diving directly on a consumer will give you the default value
+ expect(shallow()
+ .find(Consumes)
+ .dive()
+ .find(Consumer)
+ .dive()
+ .text()).to.equal('howdy!');
+ });
+
+ it('gives the actual value if one dive()s it', () => {
+ expect(shallow()
+ .find(Provides)
+ .dive()
+ .find(Provider)
+ .dive()
+ .find(Consumes)
+ .dive()
+ .find(Consumer)
+ .dive()
+ .text()).to.equal('foo');
+ });
+
+ it('does not leak values across roots', () => {
+ const wrapper = shallow();
+
+ wrapper
+ .find(Provides)
+ .dive()
+ .find(Provider)
+ .dive();
+ expect(wrapper
+ .find(Consumes)
+ .dive()
+ .find(Consumer)
+ .dive()
+ .text()).to.equal('howdy!');
+ });
+ });
+
describeIf(is('> 0.13'), 'stateless function components', () => {
it('can pass in context', () => {
const SimpleComponent = (props, context) => (
diff --git a/packages/enzyme/src/ShallowWrapper.js b/packages/enzyme/src/ShallowWrapper.js
index 7b2b65d0f..7071f21e7 100644
--- a/packages/enzyme/src/ShallowWrapper.js
+++ b/packages/enzyme/src/ShallowWrapper.js
@@ -41,6 +41,7 @@ const OPTIONS = sym('__options__');
const SET_STATE = sym('__setState__');
const ROOT_NODES = sym('__rootNodes__');
const CHILD_CONTEXT = sym('__childContext__');
+const PROVIDER_VALUES = sym('__providerValues__');
/**
* Finds all nodes in the current wrapper nodes' render trees that match the provided predicate
@@ -268,10 +269,12 @@ class ShallowWrapper {
privateSet(this, UNRENDERED, nodes);
const renderer = adapter.createRenderer({ mode: 'shallow', ...options });
privateSet(this, RENDERER, renderer);
- this[RENDERER].render(nodes, options.context);
+ const providerValues = new Map(passedOptions[PROVIDER_VALUES] || []);
+ this[RENDERER].render(nodes, options.context, { providerValues });
const renderedNode = this[RENDERER].getNode();
privateSetNodes(this, getRootNode(renderedNode));
privateSet(this, OPTIONS, options);
+ privateSet(this, PROVIDER_VALUES, providerValues);
const { instance } = renderedNode;
if (instance && !options.disableLifecycleMethods) {
@@ -298,6 +301,7 @@ class ShallowWrapper {
privateSetNodes(this, nodes);
privateSet(this, OPTIONS, root[OPTIONS]);
privateSet(this, ROOT_NODES, root[NODES]);
+ privateSet(this, PROVIDER_VALUES, null);
}
}
@@ -470,7 +474,9 @@ class ShallowWrapper {
);
}
if (props) this[UNRENDERED] = cloneElement(adapter, this[UNRENDERED], props);
- this[RENDERER].render(this[UNRENDERED], nextContext);
+ this[RENDERER].render(this[UNRENDERED], nextContext, {
+ providerValues: this[PROVIDER_VALUES],
+ });
if (shouldComponentUpdateSpy) {
shouldRender = shouldComponentUpdateSpy.getLastReturnValue();
shouldComponentUpdateSpy.restore();
@@ -1504,14 +1510,16 @@ class ShallowWrapper {
if (!isCustomComponentElement(el, adapter)) {
throw new TypeError(`ShallowWrapper::${name}() can only be called on components`);
}
- return this.wrap(el, null, {
+ const childOptions = {
...this[OPTIONS],
...options,
context: options.context || {
...this[OPTIONS].context,
...this[ROOT][CHILD_CONTEXT],
},
- });
+ };
+ privateSet(childOptions, PROVIDER_VALUES, this[ROOT][PROVIDER_VALUES]);
+ return this.wrap(el, null, childOptions);
});
}