Skip to content

Commit

Permalink
Fix hydrating renders for React 15 adapters
Browse files Browse the repository at this point in the history
  • Loading branch information
lelandrichardson committed Sep 17, 2017
1 parent 35df18a commit 460c35a
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 23 deletions.
44 changes: 33 additions & 11 deletions packages/enzyme-adapter-react-15.4/src/ReactFifteenFourAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,43 +26,57 @@ function compositeTypeToNodeType(type) {
}
}

function childrenFromInst(inst, el) {
if (inst._renderedChildren) {
return values(inst._renderedChildren);
} else if (el.props) {
return values({ '.0': el.props.children });
}
return [];
}

function nodeType(inst) {
if (inst._compositeType != null) {
return compositeTypeToNodeType(inst._compositeType);
}
return 'host';
}

function instanceToTree(inst) {
if (!inst || typeof inst !== 'object') {
return inst;
}
const el = inst._currentElement;
if (!el) {
if (el == null || el === false) {
return null;
} else if (typeof el !== 'object') {
return el;
}
if (inst._renderedChildren) {
return {
nodeType: inst._hostNode ? 'host' : compositeTypeToNodeType(inst._compositeType),
nodeType: nodeType(inst),
type: el.type,
props: el.props,
key: el.key,
ref: el.ref,
instance: inst._instance || inst._hostNode || null,
rendered: values(inst._renderedChildren).map(instanceToTree),
rendered: childrenFromInst(inst, el).map(instanceToTree),
};
}
if (inst._hostNode) {
if (typeof el !== 'object') {
return el;
}
const children = inst._renderedChildren || { '.0': el.props.children };
return {
nodeType: 'host',
nodeType: nodeType(inst),
type: el.type,
props: el.props,
key: el.key,
ref: el.ref,
instance: inst._instance || inst._hostNode || null,
rendered: values(children).map(instanceToTree),
rendered: childrenFromInst(inst, el).map(instanceToTree),
};
}
if (inst._renderedComponent) {
return {
nodeType: compositeTypeToNodeType(inst._compositeType),
nodeType: nodeType(inst),
type: el.type,
props: el.props,
key: el.key,
Expand All @@ -71,7 +85,15 @@ function instanceToTree(inst) {
rendered: instanceToTree(inst._renderedComponent),
};
}
throw new Error('Enzyme Internal Error: unknown instance encountered');
return {
nodeType: nodeType(inst),
type: el.type,
props: el.props,
key: el.key,
ref: el.ref,
instance: inst._instance || null,
rendered: childrenFromInst(inst, el).map(instanceToTree),
};
}

class ReactFifteenFourAdapter extends EnzymeAdapter {
Expand Down
39 changes: 30 additions & 9 deletions packages/enzyme-adapter-react-15/src/ReactFifteenAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,35 @@ function compositeTypeToNodeType(type) {
throw new Error(`Enzyme Internal Error: unknown composite type ${type}`);
}
}
function childrenFromInst(inst, el) {
if (inst._renderedChildren) {
return values(inst._renderedChildren);
} else if (el.props) {
return values({ '.0': el.props.children });
}
return [];
}

function nodeType(inst) {
if (inst._compositeType != null) {
return compositeTypeToNodeType(inst._compositeType);
}
return 'host';
}

function instanceToTree(inst) {
if (!inst || typeof inst !== 'object') {
return inst;
}
const el = inst._currentElement;
if (!el) {
if (el == null || el === false) {
return null;
} else if (typeof el !== 'object') {
return el;
}
if (inst._renderedChildren) {
return {
nodeType: inst._hostNode ? 'host' : compositeTypeToNodeType(inst._compositeType),
nodeType: nodeType(inst),
type: el.type,
props: el.props,
key: el.key,
Expand All @@ -47,23 +64,19 @@ function instanceToTree(inst) {
};
}
if (inst._hostNode) {
if (typeof el !== 'object') {
return el;
}
const children = inst._renderedChildren || { '.0': el.props.children };
return {
nodeType: 'host',
type: el.type,
props: el.props,
key: el.key,
ref: el.ref,
instance: inst._instance || inst._hostNode || null,
rendered: values(children).map(instanceToTree),
rendered: childrenFromInst(inst, el).map(instanceToTree),
};
}
if (inst._renderedComponent) {
return {
nodeType: compositeTypeToNodeType(inst._compositeType),
nodeType: nodeType(inst),
type: el.type,
props: el.props,
key: el.key,
Expand All @@ -72,7 +85,15 @@ function instanceToTree(inst) {
rendered: instanceToTree(inst._renderedComponent),
};
}
throw new Error('Enzyme Internal Error: unknown instance encountered');
return {
nodeType: nodeType(inst),
type: el.type,
props: el.props,
key: el.key,
ref: el.ref,
instance: inst._instance || null,
rendered: childrenFromInst(inst, el).map(instanceToTree),
};
}

class ReactFifteenAdapter extends EnzymeAdapter {
Expand Down
8 changes: 5 additions & 3 deletions packages/enzyme-test-suite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@
"enzyme-adapter-utils": "^1.0.0-beta.4",
"prop-types": "^15.5.10",
"semver": "^5.4.1",
"sinon": "^2.4.1"
"sinon": "^2.4.1",
"jsdom": "^6.1.0"
},
"peerDependencies": {
"react": "^15.5.0"
"react": "^15.5.0",
"react-dom": "^15.5.0"
}
}
}
56 changes: 56 additions & 0 deletions packages/enzyme-test-suite/test/Adapter-spec.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import './_helpers/setupAdapters';
import React from 'react';
import { expect } from 'chai';
import { renderToString } from './_helpers/react-compat';
import jsdom from 'jsdom';

import { REACT013, REACT16 } from './_helpers/version';
import configuration from 'enzyme/build/configuration';
Expand Down Expand Up @@ -35,6 +37,60 @@ function cleanNode(node) {

describe('Adapter', () => {
describeWithDOM('mounted render', () => {

function hydratedTreeMatchesUnhydrated(element) {
const markup = renderToString(element);
const dom = jsdom.jsdom(`<div id="root">${markup}</div>`);

const rendererA = adapter.createRenderer({
mode: 'mount',
attachTo: dom.querySelector('#root'),
});

rendererA.render(element);

const nodeA = rendererA.getNode();

cleanNode(nodeA);

const rendererB = adapter.createRenderer({
mode: 'mount',
});

rendererB.render(element);

const nodeB = rendererB.getNode();

cleanNode(nodeB);
expect(prettyFormat(nodeA)).to.equal(prettyFormat(nodeB));
}

it('hydrated trees match unhydrated trees', () => {
class Bam extends React.Component {
render() { return (<div>{this.props.children}</div>); }
}
class Foo extends React.Component {
render() { return (<Bam>{this.props.children}</Bam>); }
}
class One extends React.Component {
render() { return (<Foo><span><Foo /></span></Foo>); }
}
class Two extends React.Component {
render() { return (<Foo><span>2</span></Foo>); }
}
class Three extends React.Component {
render() { return (<Foo><span><div /></span></Foo>); }
}
class Four extends React.Component {
render() { return (<Foo><span>{'some string'}4{'another string'}</span></Foo>); }
}

hydratedTreeMatchesUnhydrated(<One />);
hydratedTreeMatchesUnhydrated(<Two />);
hydratedTreeMatchesUnhydrated(<Three />);
hydratedTreeMatchesUnhydrated(<Four />);
});

it('treats mixed children correctlyf', () => {
class Foo extends React.Component {
render() {
Expand Down
9 changes: 9 additions & 0 deletions packages/enzyme-test-suite/test/_helpers/react-compat.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import { is } from './version';

let createClass;
let renderToString;

if (is('>=15.5 || ^16.0.0-alpha')) {
// eslint-disable-next-line import/no-extraneous-dependencies
Expand All @@ -15,6 +16,14 @@ if (is('>=15.5 || ^16.0.0-alpha')) {
createClass = require('react').createClass;
}

if (is('^0.13.0')) {
renderToString = require('react').renderToStaticMarkup;
} else {
// eslint-disable-next-line import/no-extraneous-dependencies
renderToString = require('react-dom/server').renderToString;
}

export {
createClass,
renderToString,
};

0 comments on commit 460c35a

Please sign in to comment.