Skip to content

Commit

Permalink
Adapter Initial Implementation + React 16 Support
Browse files Browse the repository at this point in the history
  • Loading branch information
lelandrichardson committed Aug 15, 2017
1 parent 2a14aff commit baf6823
Show file tree
Hide file tree
Showing 33 changed files with 2,300 additions and 1,000 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,4 @@ env:
- REACT=0.14
- REACT=15.4
- REACT=15
- REACT=16
94 changes: 94 additions & 0 deletions docs/future/migration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Migration Guide (for React 0.13 - React 15.x)


## Root Wrapper

The initially returned wrapper used to be around the element passed
into the `mount` API, and for `shallow` it was around the root node of the rendered output of the element passed in. After the upgrade, the
two APIs are now symmetrical, starting off

```js
const x = 'x';
const Foo = props => <div inner={props.outer} />
const wrapper = mount(<Foo outer={x} />);
```

```js
expect(wrapper.props()).to.deep.equal({ outer: x });
```

## Refs

Refs no longer return a "wrapper". They return what the ref would actually be.


## Keys

keys no longer work? we should maybe fix this in the spec...


## for shallow, getNode() was renamed to getElement()

## for mount, getNode() should not be used. instance() does what it used to.

## for mount, getElement() will return the root JSX element

## what getNode() returns

we need to keep in mind that `getElement()` will no longer be referentially equal to what it was before.

## Updates are required

```
wrapper.find('.async-btn').simulate('click');
setImmediate(() => {
// this didn't used to be needed
wrapper.update(); // TODO(lmr): this is a breaking change...
expect(wrapper.find('.show-me').length).to.equal(1);
done();
});
```




## Enzyme.use




# Migration Guide (for React 16)

## Stateless Functional Components

SFCs actually go down a different code path in react 16, which means that they
dont have "instances" associated with them, which means there are a couple of things
that we used to be able to do with enzyme + SFCs that will just no longer work.

We could fix a lot of this if there was a reliable way to get from an SFC "fiber" to
the corresponding DOM element that it renders.

## Strings vs. Numbers

React 16 converts numbers to strings very early on. we can't change this. this will change
some behavior in enzyme but we are considering this the "correct" behavior.











# Left to do:

- move adapters into standalone packages
- x create Enzyme.use API
- x create api to inject adapter per use
- x make sure all react dependence is moved into adapters
- x make tests run for all adapters
- export tests for 3rd party adapters to run
- check the targetApiVersion returned by the adapter and use the semver library
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@
"test:watch": "mocha --recursive --watch test",
"test:karma": "karma start",
"test:env": "sh ./example-test.sh",
"test:all": "npm run react:13 && npm run test:only && npm run react:14 && npm run test:only && npm run react:15.4 && npm run test:only && npm run react:15 && npm run test:only",
"test:all": "npm run react:13 && npm run test:only && npm run react:14 && npm run test:only && npm run react:15.4 && npm run test:only && npm run react:15 && npm run test:only && npm run react:16 && npm run test:only",
"clean-local-npm": "rimraf node_modules/.bin/npm node_modules/.bin/npm.cmd",
"react:clean": "npm run clean-local-npm && rimraf node_modules/react node_modules/react-dom node_modules/react-addons-test-utils node_modules/react-test-renderer && npm prune",
"react:13": "npm run react:clean && npm install && npm i --no-save react@0.13",
"react:14": "npm run react:clean && npm install && npm i --no-save react@0.14 react-dom@0.14 react-addons-test-utils@0.14",
"react:15.4": "npm run react:clean && npm install && npm i --no-save react@15.4 react-dom@15.4 react-addons-test-utils@15.4",
"react:15": "npm run react:clean && npm install && npm i --no-save react@15 react-dom@15 create-react-class@15 react-test-renderer@^15.5.4",
"react:16": "npm run react:clean && npm install && npm i --no-save react@16.0.0-alpha.13 react-dom@16.0.0-alpha.13 create-react-class@15.6.0 react-test-renderer@16.0.0-alpha.13",
"docs:clean": "rimraf _book",
"docs:lint": "eslint --ext md --config .eslintrc-markdown .",
"docs:prepare": "gitbook install",
Expand Down Expand Up @@ -66,6 +67,8 @@
"object.entries": "^1.0.4",
"object.values": "^1.0.4",
"prop-types": "^15.5.10",
"raf": "^3.3.2",
"semver": "^5.3.0",
"uuid": "^3.0.1"
},
"devDependencies": {
Expand Down Expand Up @@ -102,13 +105,12 @@
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^1.8.1",
"mocha": "^3.5.0",
"react": "0.13.x || 0.14.x || ^15.0.0-0 || 15.x",
"rimraf": "^2.6.1",
"safe-publish-latest": "^1.1.1",
"sinon": "^2.4.1",
"webpack": "^1.13.3"
},
"peerDependencies": {
"react": "0.13.x || 0.14.x || ^15.0.0-0 || 15.x"
"react": "0.13.x || 0.14.x || ^15.0.0-0 || 15.x || 16.x || ^16.0.0-alpha"
}
}
25 changes: 25 additions & 0 deletions setupAdapters.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* eslint global-require: 0 */
/**
* This file is needed only because we run our unit tests on multiple
* versions of React at a time. This file basically figures out which
* version of React is loaded, and configures enzyme to use the right
* corresponding adapter.
*/
const Version = require('./src/version');
const Enzyme = require('./src');

let Adapter = null;

if (Version.REACT013) {
Adapter = require('./src/adapters/ReactThirteenAdapter');
} else if (Version.REACT014) {
Adapter = require('./src/adapters/ReactFourteenAdapter');
} else if (Version.REACT155) {
Adapter = require('./src/adapters/ReactFifteenAdapter');
} else if (Version.REACT15) {
Adapter = require('./src/adapters/ReactFifteenFourAdapter');
} else if (Version.REACT16) {
Adapter = require('./src/adapters/ReactSixteenAdapter');
}

Enzyme.configure({ adapter: new Adapter() });
74 changes: 2 additions & 72 deletions src/Debug.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,12 @@
import without from 'lodash/without';
import escape from 'lodash/escape';
import compact from 'lodash/compact';
import objectValues from 'object.values';
import functionName from 'function.prototype.name';

import {
childrenOfNode,
} from './ShallowTraversal';
import {
renderedChildrenOfInst,
} from './MountedTraversal';
import {
isDOMComponent,
isCompositeComponent,
isElement,
} from './react-compat';
import {
internalInstance,
propsOfNode,
} from './Utils';
import { REACT013 } from './version';
childrenOfNode,
} from './RSTTraversal';

export function typeName(node) {
return typeof node.type === 'function'
Expand Down Expand Up @@ -83,60 +70,3 @@ export function debugNode(node, indentLength = 2, options = {}) {
export function debugNodes(nodes, options = {}) {
return nodes.map(node => debugNode(node, undefined, options)).join('\n\n\n');
}

export function debugInst(inst, indentLength = 2, options = {}) {
if (typeof inst === 'string' || typeof inst === 'number') return escape(inst);
if (!inst) return '';

if (inst._stringText) {
return inst._stringText;
}

if (!inst.getPublicInstance) {
const internal = internalInstance(inst);
return debugInst(internal, indentLength, options);
}
const publicInst = inst.getPublicInstance();

if (typeof publicInst === 'string' || typeof publicInst === 'number') return escape(publicInst);
if (!publicInst && !inst._renderedComponent) return '';

// do stuff with publicInst
const currentElement = inst._currentElement;
const type = typeName(currentElement);
const props = options.ignoreProps ? '' : propsString(currentElement);
const children = [];
if (isDOMComponent(publicInst)) {
const renderedChildren = renderedChildrenOfInst(inst);
if (!renderedChildren) {
children.push(...childrenOfNode(currentElement));
} else {
children.push(...objectValues(renderedChildren));
}
} else if (
!REACT013 &&
isElement(currentElement) &&
typeof currentElement.type === 'function'
) {
children.push(inst._renderedComponent);
} else if (
REACT013 &&
isCompositeComponent(publicInst)
) {
children.push(inst._renderedComponent);
}

const childrenStrs = compact(children.map(n => debugInst(n, indentLength, options)));

const beforeProps = props ? ' ' : '';
const nodeClose = childrenStrs.length ? `</${type}>` : '/>';
const afterProps = childrenStrs.length
? '>'
: ' ';
const childrenIndented = indentChildren(childrenStrs, indentLength);
return `<${type}${beforeProps}${props}${afterProps}${childrenIndented}${nodeClose}`;
}

export function debugInsts(insts, options = {}) {
return insts.map(inst => debugInst(inst, undefined, options)).join('\n\n\n');
}

0 comments on commit baf6823

Please sign in to comment.