@pisano/enzyme-adapter-react-16
Version:
JavaScript Testing utilities for React
461 lines (398 loc) • 16.1 kB
JavaScript
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _object = require('object.assign');
var _object2 = _interopRequireDefault(_object);
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _reactDom = require('react-dom');
var _reactDom2 = _interopRequireDefault(_reactDom);
var _server = require('react-dom/server');
var _server2 = _interopRequireDefault(_server);
var _shallow = require('react-test-renderer/shallow');
var _shallow2 = _interopRequireDefault(_shallow);
var _testUtils = require('react-dom/test-utils');
var _testUtils2 = _interopRequireDefault(_testUtils);
var _enzyme = require('@pisano/enzyme');
var _enzymeAdapterUtils = require('@pisano/enzyme-adapter-utils');
var _reflection = require('react-reconciler/reflection');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /* eslint no-use-before-define: 0 */
// eslint-disable-next-line import/no-unresolved, import/extensions
// eslint-disable-next-line import/no-unresolved, import/extensions
// eslint-disable-next-line import/no-unresolved, import/extensions
var HostRoot = 3;
var ClassComponent = 2;
var Fragment = 10;
var FunctionalComponent = 1;
var HostPortal = 4;
var HostComponent = 5;
var HostText = 6;
var Mode = 11;
var ContextConsumer = 12;
var ContextProvider = 13;
function nodeAndSiblingsArray(nodeWithSibling) {
var array = [];
var node = nodeWithSibling;
while (node != null) {
array.push(node);
node = node.sibling;
}
return array;
}
function flatten(arr) {
var result = [];
var stack = [{ i: 0, array: arr }];
while (stack.length) {
var n = stack.pop();
while (n.i < n.array.length) {
var el = n.array[n.i];
n.i += 1;
if (Array.isArray(el)) {
stack.push(n);
stack.push({ i: 0, array: el });
break;
}
result.push(el);
}
}
return result;
}
function toTree(vnode) {
if (vnode == null) {
return null;
}
// TODO(lmr): I'm not really sure I understand whether or not this is what
// i should be doing, or if this is a hack for something i'm doing wrong
// somewhere else. Should talk to sebastian about this perhaps
var node = (0, _reflection.findCurrentFiberUsingSlowPath)(vnode);
switch (node.tag) {
case HostRoot:
// 3
return toTree(node.child);
case HostPortal:
// 4
return toTree(node.child);
case ClassComponent:
return {
nodeType: 'class',
type: node.type,
props: (0, _object2['default'])({}, node.memoizedProps),
key: (0, _enzymeAdapterUtils.ensureKeyOrUndefined)(node.key),
ref: node.ref,
instance: node.stateNode,
rendered: childrenToTree(node.child)
};
case FunctionalComponent:
// 1
return {
nodeType: 'function',
type: node.type,
props: (0, _object2['default'])({}, node.memoizedProps),
key: (0, _enzymeAdapterUtils.ensureKeyOrUndefined)(node.key),
ref: node.ref,
instance: null,
rendered: childrenToTree(node.child)
};
case HostComponent:
{
// 5
var renderedNodes = flatten(nodeAndSiblingsArray(node.child).map(toTree));
if (renderedNodes.length === 0) {
renderedNodes = [node.memoizedProps.children];
}
return {
nodeType: 'host',
type: node.type,
props: (0, _object2['default'])({}, node.memoizedProps),
key: (0, _enzymeAdapterUtils.ensureKeyOrUndefined)(node.key),
ref: node.ref,
instance: node.stateNode,
rendered: renderedNodes
};
}
case HostText:
// 6
return node.memoizedProps;
case Fragment: // 10
case Mode: // 11
case ContextProvider: // 13
case ContextConsumer:
// 12
return childrenToTree(node.child);
default:
throw new Error('Enzyme Internal Error: unknown node with tag ' + String(node.tag));
}
}
function childrenToTree(node) {
if (!node) {
return null;
}
var children = nodeAndSiblingsArray(node);
if (children.length === 0) {
return null;
} else if (children.length === 1) {
return toTree(children[0]);
}
return flatten(children.map(toTree));
}
function _nodeToHostNode(_node) {
// NOTE(lmr): node could be a function component
// which wont have an instance prop, but we can get the
// host node associated with its return value at that point.
// Although this breaks down if the return value is an array,
// as is possible with React 16.
var node = _node;
while (node && !Array.isArray(node) && node.instance === null) {
node = node.rendered;
}
if (Array.isArray(node)) {
// TODO(lmr): throw warning regarding not being able to get a host node here
throw new Error('Trying to get host node of an array');
}
// if the SFC returned null effectively, there is no host node.
if (!node) {
return null;
}
return _reactDom2['default'].findDOMNode(node.instance);
}
var ReactSixteenAdapter = function (_EnzymeAdapter) {
_inherits(ReactSixteenAdapter, _EnzymeAdapter);
function ReactSixteenAdapter() {
_classCallCheck(this, ReactSixteenAdapter);
var _this = _possibleConstructorReturn(this, (ReactSixteenAdapter.__proto__ || Object.getPrototypeOf(ReactSixteenAdapter)).call(this));
_this.options = (0, _object2['default'])({}, _this.options, {
enableComponentDidUpdateOnSetState: true
});
return _this;
}
_createClass(ReactSixteenAdapter, [{
key: 'createMountRenderer',
value: function () {
function createMountRenderer(options) {
(0, _enzymeAdapterUtils.assertDomAvailable)('mount');
var domNode = options.attachTo || global.document.createElement('div');
var instance = null;
return {
render: function () {
function render(el, context, callback) {
if (instance === null) {
var ReactWrapperComponent = (0, _enzymeAdapterUtils.createMountWrapper)(el, options);
var wrappedEl = _react2['default'].createElement(ReactWrapperComponent, {
Component: el.type,
props: el.props,
context: context
});
instance = _reactDom2['default'].render(wrappedEl, domNode);
if (typeof callback === 'function') {
callback();
}
} else {
instance.setChildProps(el.props, context, callback);
}
}
return render;
}(),
unmount: function () {
function unmount() {
_reactDom2['default'].unmountComponentAtNode(domNode);
instance = null;
}
return unmount;
}(),
getNode: function () {
function getNode() {
return instance ? toTree(instance._reactInternalFiber).rendered : null;
}
return getNode;
}(),
simulateEvent: function () {
function simulateEvent(node, event, mock) {
var mappedEvent = (0, _enzymeAdapterUtils.mapNativeEventNames)(event);
var eventFn = _testUtils2['default'].Simulate[mappedEvent];
if (!eventFn) {
throw new TypeError('ReactWrapper::simulate() event \'' + String(event) + '\' does not exist');
}
// eslint-disable-next-line react/no-find-dom-node
eventFn(_nodeToHostNode(node), mock);
}
return simulateEvent;
}(),
batchedUpdates: function () {
function batchedUpdates(fn) {
return fn();
// return ReactDOM.unstable_batchedUpdates(fn);
}
return batchedUpdates;
}()
};
}
return createMountRenderer;
}()
}, {
key: 'createShallowRenderer',
value: function () {
function createShallowRenderer() /* options */{
var renderer = new _shallow2['default']();
var isDOM = false;
var cachedNode = null;
return {
render: function () {
function render(el, context) {
cachedNode = el;
/* eslint consistent-return: 0 */
if (typeof el.type === 'string') {
isDOM = true;
} else {
isDOM = false;
return (0, _enzymeAdapterUtils.withSetStateAllowed)(function () {
return renderer.render(el, context);
});
}
}
return render;
}(),
unmount: function () {
function unmount() {
renderer.unmount();
}
return unmount;
}(),
getNode: function () {
function getNode() {
if (isDOM) {
return (0, _enzymeAdapterUtils.elementToTree)(cachedNode);
}
var output = renderer.getRenderOutput();
return {
nodeType: (0, _enzymeAdapterUtils.nodeTypeFromType)(cachedNode.type),
type: cachedNode.type,
props: cachedNode.props,
key: (0, _enzymeAdapterUtils.ensureKeyOrUndefined)(cachedNode.key),
ref: cachedNode.ref,
instance: renderer._instance,
rendered: Array.isArray(output) ? flatten(output).map(_enzymeAdapterUtils.elementToTree) : (0, _enzymeAdapterUtils.elementToTree)(output)
};
}
return getNode;
}(),
simulateEvent: function () {
function simulateEvent(node, event) {
for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
args[_key - 2] = arguments[_key];
}
var handler = node.props[(0, _enzymeAdapterUtils.propFromEvent)(event)];
if (handler) {
(0, _enzymeAdapterUtils.withSetStateAllowed)(function () {
// TODO(lmr): create/use synthetic events
// TODO(lmr): emulate React's event propagation
// ReactDOM.unstable_batchedUpdates(() => {
handler.apply(undefined, _toConsumableArray(args));
// });
});
}
}
return simulateEvent;
}(),
batchedUpdates: function () {
function batchedUpdates(fn) {
return fn();
// return ReactDOM.unstable_batchedUpdates(fn);
}
return batchedUpdates;
}()
};
}
return createShallowRenderer;
}()
}, {
key: 'createStringRenderer',
value: function () {
function createStringRenderer(options) {
return {
render: function () {
function render(el, context) {
if (options.context && (el.type.contextTypes || options.childContextTypes)) {
var childContextTypes = (0, _object2['default'])({}, el.type.contextTypes || {}, options.childContextTypes);
var ContextWrapper = (0, _enzymeAdapterUtils.createRenderWrapper)(el, context, childContextTypes);
return _server2['default'].renderToStaticMarkup(_react2['default'].createElement(ContextWrapper));
}
return _server2['default'].renderToStaticMarkup(el);
}
return render;
}()
};
}
return createStringRenderer;
}()
// Provided a bag of options, return an `EnzymeRenderer`. Some options can be implementation
// specific, like `attach` etc. for React, but not part of this interface explicitly.
// eslint-disable-next-line class-methods-use-this, no-unused-vars
}, {
key: 'createRenderer',
value: function () {
function createRenderer(options) {
switch (options.mode) {
case _enzyme.EnzymeAdapter.MODES.MOUNT:
return this.createMountRenderer(options);
case _enzyme.EnzymeAdapter.MODES.SHALLOW:
return this.createShallowRenderer(options);
case _enzyme.EnzymeAdapter.MODES.STRING:
return this.createStringRenderer(options);
default:
throw new Error('Enzyme Internal Error: Unrecognized mode: ' + String(options.mode));
}
}
return createRenderer;
}()
// converts an RSTNode to the corresponding JSX Pragma Element. This will be needed
// in order to implement the `Wrapper.mount()` and `Wrapper.shallow()` methods, but should
// be pretty straightforward for people to implement.
// eslint-disable-next-line class-methods-use-this, no-unused-vars
}, {
key: 'nodeToElement',
value: function () {
function nodeToElement(node) {
if (!node || (typeof node === 'undefined' ? 'undefined' : _typeof(node)) !== 'object') return null;
return _react2['default'].createElement(node.type, (0, _enzymeAdapterUtils.propsWithKeysAndRef)(node));
}
return nodeToElement;
}()
}, {
key: 'elementToNode',
value: function () {
function elementToNode(element) {
return (0, _enzymeAdapterUtils.elementToTree)(element);
}
return elementToNode;
}()
}, {
key: 'nodeToHostNode',
value: function () {
function nodeToHostNode(node) {
return _nodeToHostNode(node);
}
return nodeToHostNode;
}()
}, {
key: 'isValidElement',
value: function () {
function isValidElement(element) {
return _react2['default'].isValidElement(element);
}
return isValidElement;
}()
}, {
key: 'createElement',
value: function () {
function createElement() {
return _react2['default'].createElement.apply(_react2['default'], arguments);
}
return createElement;
}()
}]);
return ReactSixteenAdapter;
}(_enzyme.EnzymeAdapter);
module.exports = ReactSixteenAdapter;