From 7efeda383c3c914f038ec0005ca493ed591b9d47 Mon Sep 17 00:00:00 2001 From: Ian Christian Myers Date: Tue, 8 Mar 2016 18:44:50 -0800 Subject: [PATCH] Add compatibility for React 15 RC1 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. --- package.json | 6 +++--- src/Debug.js | 7 +++---- src/MountedTraversal.js | 19 ++++++++++++------- src/ReactWrapper.js | 4 ++-- src/ShallowTraversal.js | 4 ++++ src/Utils.js | 19 +++++++++++++++++-- 6 files changed, 41 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index dab08a221..8c3ab61bd 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/Debug.js b/src/Debug.js index 75a3d2876..cdbb7bff5 100644 --- a/src/Debug.js +++ b/src/Debug.js @@ -2,7 +2,6 @@ import { childrenOfNode, } from './ShallowTraversal'; import { - internalInstance, renderedChildrenOfInst, } from './MountedTraversal'; import { @@ -11,12 +10,13 @@ import { isElement, } from './react-compat'; import { + internalInstance, propsOfNode, } from './Utils'; 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) { @@ -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); @@ -104,7 +103,7 @@ export function debugInst(inst, indentLength = 2) { children.push(...objectValues(renderedChildren)); } } else if ( - REACT014 && + !REACT013 && isElement(currentElement) && typeof currentElement.type === 'function' ) { diff --git a/src/MountedTraversal.js b/src/MountedTraversal.js index d2a9f6c82..660d53a82 100644 --- a/src/MountedTraversal.js +++ b/src/MountedTraversal.js @@ -1,6 +1,7 @@ import isEmpty from 'lodash/isEmpty'; import isSubset from 'is-subset'; import { + internalInstance, coercePropValue, nodeEqual, propsOfNode, @@ -20,11 +21,7 @@ import { isElement, findDOMNode, } from './react-compat'; -import { REACT013, REACT014 } from './version'; - -export function internalInstance(inst) { - return inst._reactInternalInstance; -} +import { REACT013 } from './version'; export function getNode(inst) { if (!inst || inst._store || typeof inst === 'string') { @@ -36,6 +33,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; } @@ -75,6 +75,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); @@ -107,6 +111,7 @@ export function childrenOfInstInternal(inst) { const internal = internalInstance(inst); return childrenOfInstInternal(internal); } + const publicInst = inst.getPublicInstance(); const currentElement = inst._currentElement; if (isDOMComponent(publicInst)) { @@ -124,7 +129,7 @@ export function childrenOfInstInternal(inst) { } return children; } else if ( - REACT014 && + !REACT013 && isElement(currentElement) && typeof currentElement.type === 'function' ) { @@ -259,7 +264,7 @@ function findAllInRenderedTreeInternal(inst, test) { ); } } else if ( - REACT014 && + !REACT013 && isElement(currentElement) && typeof currentElement.type === 'function' ) { diff --git a/src/ReactWrapper.js b/src/ReactWrapper.js index 7281ed115..9f1240cd3 100644 --- a/src/ReactWrapper.js +++ b/src/ReactWrapper.js @@ -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; } @@ -343,7 +342,8 @@ export default class ReactWrapper { html() { return this.single(n => { const node = findDOMNode(n); - return node === null ? null : node.outerHTML.replace(/\sdata-reactid+="[^"]+"/g, ''); + return node === null ? null : + node.outerHTML.replace(/\sdata-(reactid|reactroot)+="([^"]*)+"/g, ''); }); } diff --git a/src/ShallowTraversal.js b/src/ShallowTraversal.js index 3eb2bc9e8..97b1291fe 100644 --- a/src/ShallowTraversal.js +++ b/src/ShallowTraversal.js @@ -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) { diff --git a/src/Utils.js b/src/Utils.js index 7243f832e..21c0849d9 100644 --- a/src/Utils.js +++ b/src/Utils.js @@ -7,9 +7,18 @@ import { } from './react-compat'; import { REACT013, - REACT014, + REACT15, } from './version'; +function internalInstanceKey(node) { + return Object.keys(Object(node)).filter(key => key.match(/^__reactInternalInstance\$/))[0]; +} + +export function internalInstance(inst) { + return inst._reactInternalInstance || + inst[internalInstanceKey(inst)]; +} + export function propsOfNode(node) { if (REACT013 && node && node._store) { return (node._store.props) || {}; @@ -17,6 +26,12 @@ export function propsOfNode(node) { if (node && node._reactInternalComponent && node._reactInternalComponent._currentElement) { return (node._reactInternalComponent._currentElement.props) || {}; } + if (REACT15 && node) { + if (internalInstance(node) && internalInstance(node)._currentElement) { + return (internalInstance(node)._currentElement.props) || {}; + } + } + return (node && node.props) || {}; } @@ -232,7 +247,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';