Skip to content

Commit

Permalink
Add compatibility for React 15 RC1
Browse files Browse the repository at this point in the history
This adds compatibility for React 15 RC1. The most painful/icky
change is in retrieving the internal instance, which has a random
number appended to its key.

There's also a hack to work around a bug in React 15 where warnings
on ref and key are throwing for nodes with string types.
  • Loading branch information
iancmyers committed Mar 9, 2016
1 parent 4c903a7 commit 9d6e3cf
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 22 deletions.
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
"check": "npm run lint && npm run test:all",
"build": "babel src --out-dir build",
"test": "npm run lint && npm run test:only",
"test:only": "mocha --require withDom.js --compilers js:babel-core/register --recursive test/*.js",
"test:single": "mocha --require withDom.js --compilers js:babel-core/register --watch",
"test:watch": "mocha --require withDom.js --compilers js:babel-core/register --recursive test/*.js --watch",
"test:only": "mocha --require withDom.js --compilers js:babel-core/register --recursive test/*.js --reporter dot",
"test:single": "mocha --require withDom.js --compilers js:babel-core/register --watch --reporter dot",
"test:watch": "mocha --require withDom.js --compilers js:babel-core/register --recursive test/*.js --watch --reporter dot",
"test:env": "sh ./example-test.sh",
"test:all": "npm run react:13 && npm test && npm run react:14 && npm test && npm run react:15 && npm test",
"react:clean": "rimraf node_modules/react node_modules/react-dom node_modules/react-addons-test-utils",
Expand Down
5 changes: 2 additions & 3 deletions src/Debug.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
import without from 'lodash/without';
import escape from 'lodash/escape';
import compact from 'lodash/compact';
import { REACT013, REACT014 } from './version';
import { REACT013 } from './version';
import objectValues from 'object.values';

export function typeName(node) {
Expand Down Expand Up @@ -85,7 +85,6 @@ export function debugInst(inst, indentLength = 2) {
const internal = internalInstance(inst);
return debugInst(internal, indentLength);
}

const publicInst = inst.getPublicInstance();

if (typeof publicInst === 'string' || typeof publicInst === 'number') return escape(publicInst);
Expand All @@ -104,7 +103,7 @@ export function debugInst(inst, indentLength = 2) {
children.push(...objectValues(renderedChildren));
}
} else if (
REACT014 &&
(!REACT013) &&
isElement(currentElement) &&
typeof currentElement.type === 'function'
) {
Expand Down
17 changes: 13 additions & 4 deletions src/MountedTraversal.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ import {
isElement,
findDOMNode,
} from './react-compat';
import { REACT013, REACT014 } from './version';
import { REACT013 } from './version';

export function internalInstance(inst) {
return inst._reactInternalInstance;
return inst._reactInternalInstance ||
inst[Object.keys(inst).find(key => key.match(/^__reactInternalInstance\$/))];
}

export function getNode(inst) {
Expand All @@ -36,6 +37,9 @@ export function getNode(inst) {
if (internalInstance(inst)) {
return internalInstance(inst)._currentElement;
}
if (inst._reactInternalInstance) {
return inst._reactInternalInstance._currentElement;
}
if (inst._reactInternalComponent) {
return inst._reactInternalComponent._currentElement;
}
Expand Down Expand Up @@ -75,6 +79,10 @@ export function instHasProperty(inst, propKey, stringifiedPropValue) {
if (!isDOMComponent(inst)) return false;
const node = getNode(inst);
const nodeProps = propsOfNode(node);
const descriptor = Object.getOwnPropertyDescriptor(nodeProps, propKey);
if (descriptor && descriptor.get) {
return false;
}
const nodePropValue = nodeProps[propKey];

const propValue = coercePropValue(propKey, stringifiedPropValue);
Expand Down Expand Up @@ -107,6 +115,7 @@ export function childrenOfInstInternal(inst) {
const internal = internalInstance(inst);
return childrenOfInstInternal(internal);
}

const publicInst = inst.getPublicInstance();
const currentElement = inst._currentElement;
if (isDOMComponent(publicInst)) {
Expand All @@ -124,7 +133,7 @@ export function childrenOfInstInternal(inst) {
}
return children;
} else if (
REACT014 &&
(!REACT013) &&
isElement(currentElement) &&
typeof currentElement.type === 'function'
) {
Expand Down Expand Up @@ -259,7 +268,7 @@ function findAllInRenderedTreeInternal(inst, test) {
);
}
} else if (
REACT014 &&
(!REACT013) &&
isElement(currentElement) &&
typeof currentElement.type === 'function'
) {
Expand Down
1 change: 0 additions & 1 deletion src/ReactWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,6 @@ export default class ReactWrapper {
const predicate = Array.isArray(nodeOrNodes)
? other => containsChildrenSubArray(instEqual, other, nodeOrNodes)
: other => instEqual(nodeOrNodes, other);

return findWhereUnwrapped(this, predicate).length > 0;
}

Expand Down
4 changes: 4 additions & 0 deletions src/ShallowTraversal.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ export function nodeHasId(node, id) {
export function nodeHasProperty(node, propKey, stringifiedPropValue) {
const nodeProps = propsOfNode(node);
const propValue = coercePropValue(propKey, stringifiedPropValue);
const descriptor = Object.getOwnPropertyDescriptor(nodeProps, propKey);
if (descriptor && descriptor.get) {
return false;
}
const nodePropValue = nodeProps[propKey];

if (nodePropValue === undefined) {
Expand Down
11 changes: 9 additions & 2 deletions src/Utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
} from './react-compat';
import {
REACT013,
REACT014,
REACT15,
} from './version';

export function propsOfNode(node) {
Expand All @@ -17,6 +17,13 @@ export function propsOfNode(node) {
if (node && node._reactInternalComponent && node._reactInternalComponent._currentElement) {
return (node._reactInternalComponent._currentElement.props) || {};
}
if (REACT15 && node) {
const instanceKey = Object.keys(node).find(key => key.match(/^__reactInternalInstance\$/));
if (node && node[instanceKey] && node[instanceKey]._currentElement) {
return (node[instanceKey]._currentElement.props) || {};
}
}

return (node && node.props) || {};
}

Expand Down Expand Up @@ -232,7 +239,7 @@ export function mapNativeEventNames(event) {
volumechange: 'volumeChange',
};

if (REACT014) {
if (!REACT013) {
// these could not be simulated in React 0.13:
// https://github.com/facebook/react/issues/1297
nativeToReactEventMap.mouseenter = 'mouseEnter';
Expand Down
57 changes: 48 additions & 9 deletions test/ReactWrapper-spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describeWithDOM, describeIf } from './_helpers';
import { describeWithDOM, describeIf, itIf } from './_helpers';
import React from 'react';
import { expect } from 'chai';
import {
Expand All @@ -7,7 +7,7 @@ import {
ReactWrapper,
} from '../src/';
import sinon from 'sinon';
import { REACT013 } from '../src/version';
import { REACT013, REACT15 } from '../src/version';

describeWithDOM('mount', () => {

Expand Down Expand Up @@ -449,14 +449,14 @@ describeWithDOM('mount', () => {

const selector = 'blah';
const wrapper = mount(<SFC selector={selector} />);
const foundSpan = wrapper.findWhere(n => (
n.type() === 'span' && n.props()['data-foo'] === selector
));
const foundSpan = wrapper.findWhere(n => {
return n.type() === 'span' && n.props()['data-foo'] === selector
});
expect(foundSpan.type()).to.equal('span');

const foundNotSpan = wrapper.findWhere(n => (
n.type() !== 'span' && n.props()['data-foo'] === selector
));
const foundNotSpan = wrapper.findWhere(n => {
return n.type() !== 'span' && n.props()['data-foo'] === selector;
});
expect(foundNotSpan.type()).to.equal('i');
});

Expand Down Expand Up @@ -1632,7 +1632,44 @@ describeWithDOM('mount', () => {
});
});

describe('.html()', () => {
describeIf(REACT15, '.html()', () => {
it('should return html of straight DOM elements', () => {
const wrapper = mount(
<div className="test">
<span>Hello World!</span>
</div>
);
expect(wrapper.html()).to.equal(
`<div data-reactroot="" class="test"><span>Hello World!</span></div>`
);
});

it('should render out nested composite components', () => {
class Foo extends React.Component {
render() {
return (<div className="in-foo" />);
}
}
class Bar extends React.Component {
render() {
return (
<div className="in-bar">
<Foo />
</div>
);
}
}
const wrapper = mount(<Bar />);
expect(wrapper.html()).to.equal(
`<div data-reactroot="" class="in-bar"><div class="in-foo"></div></div>`
);
expect(wrapper.find(Foo).html()).to.equal(
`<div class="in-foo"></div>`
);
});
})

describeIf(!REACT15, '.html()', () => {
it('should return html of straight DOM elements', () => {
const wrapper = mount(
<div className="test">
Expand Down Expand Up @@ -1667,6 +1704,8 @@ describeWithDOM('mount', () => {
`<div class="in-foo"></div>`
);
});


});

describe('.render()', () => {
Expand Down

0 comments on commit 9d6e3cf

Please sign in to comment.