diff --git a/docs/api/render.md b/docs/api/render.md index dab1cf262..3123b25c3 100644 --- a/docs/api/render.md +++ b/docs/api/render.md @@ -19,7 +19,6 @@ constructors. import { render } from 'enzyme'; describe('', () => { - it('renders three `.foo-bar`s', () => { const wrapper = render(); expect(wrapper.find('.foo-bar')).to.have.length(3); @@ -30,5 +29,19 @@ describe('', () => { expect(wrapper.text()).to.contain("unique"); }); + it('can pass in context', () => { + const SimpleComponent = React.createClass({ + contextTypes: { + name: React.PropTypes.string, + }, + render() { + return
{this.context.name}
; + }, + }); + + const context = { name: 'foo' }; + const wrapper = render(, { context }); + expect(wrapper.text()).to.equal('foo'); + }); }); ``` diff --git a/src/render.js b/src/render.js deleted file mode 100644 index 0ba17df60..000000000 --- a/src/render.js +++ /dev/null @@ -1,19 +0,0 @@ -import cheerio from 'cheerio'; -import { renderToStaticMarkup } from './react-compat'; - -/** - * Renders a react component into static HTML and provides a cheerio wrapper around it. This is - * somewhat asymmetric with `mount` and `shallow`, which don't use any external libraries, but - * Cheerio's API is pretty close to what we actually want and has a significant amount of utility - * that would be recreating the wheel if we didn't use it. - * - * I think there are a lot of good use cases to use `render` instead of `shallow` or `mount`, and - * thus I'd like to keep this API in here even though it's not really "ours". - * - * @param node - * @returns {Cheerio} - */ -export default function render(node) { - const html = renderToStaticMarkup(node); - return cheerio.load(html).root(); -} diff --git a/src/render.jsx b/src/render.jsx new file mode 100644 index 000000000..ac98fa359 --- /dev/null +++ b/src/render.jsx @@ -0,0 +1,46 @@ +import React from 'react'; +import objectAssign from 'object.assign'; +import cheerio from 'cheerio'; +import { renderToStaticMarkup } from './react-compat'; + +/** + * Renders a react component into static HTML and provides a cheerio wrapper around it. This is + * somewhat asymmetric with `mount` and `shallow`, which don't use any external libraries, but + * Cheerio's API is pretty close to what we actually want and has a significant amount of utility + * that would be recreating the wheel if we didn't use it. + * + * I think there are a lot of good use cases to use `render` instead of `shallow` or `mount`, and + * thus I'd like to keep this API in here even though it's not really "ours". + * + * @param node + * @param options + * @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 = {}) { + if (options.context && (node.type.contextTypes || options.childContextTypes)) { + const childContextTypes = objectAssign( + {}, + node.type.contextTypes || {}, + options.childContextTypes, + ); + const ContextWrapper = createContextWrapperForNode(node, options.context, childContextTypes); + const html = renderToStaticMarkup(); + return cheerio.load(html).root(); + } + const html = renderToStaticMarkup(node); + return cheerio.load(html).root(); +} diff --git a/test/staticRender-spec.jsx b/test/staticRender-spec.jsx new file mode 100644 index 000000000..39a1c663d --- /dev/null +++ b/test/staticRender-spec.jsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { expect } from 'chai'; +import { describeWithDOM, describeIf } from './_helpers'; +import { render } from '../src/'; +import { REACT013 } from '../src/version'; + +describeWithDOM('render', () => { + describeIf(!REACT013, 'context', () => { + it('can pass in context', () => { + const SimpleComponent = React.createClass({ + contextTypes: { + name: React.PropTypes.string, + }, + render() { + return
{this.context.name}
; + }, + }); + + const context = { name: 'foo' }; + const wrapper = render(, { context }); + expect(wrapper.text()).to.equal('foo'); + }); + it('can pass context to the child of mounted component', () => { + const SimpleComponent = React.createClass({ + contextTypes: { + name: React.PropTypes.string, + }, + render() { + return
{this.context.name}
; + }, + }); + const ComplexComponent = React.createClass({ + render() { + return
; + }, + }); + + const childContextTypes = { + name: React.PropTypes.string.isRequired, + }; + const context = { name: 'foo' }; + const wrapper = render(, { context, childContextTypes }); + expect(wrapper.children()).to.have.length(1); + expect(wrapper.children().first().text()).to.equal('foo'); + }); + it('should not throw if context is passed in but contextTypes is missing', () => { + const SimpleComponent = React.createClass({ + render() { + return
{this.context.name}
; + }, + }); + + const context = { name: 'foo' }; + expect(() => render(, { context })).to.not.throw(Error); + }); + }); +});