rails-assets-enzyme 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/lib/rails/assets/enzyme.rb +9 -0
- data/lib/rails/assets/enzyme/version.rb +7 -0
- data/vendor/assets/javascripts/Debug.js +133 -0
- data/vendor/assets/javascripts/MountedTraversal.js +305 -0
- data/vendor/assets/javascripts/ReactWrapper.js +777 -0
- data/vendor/assets/javascripts/ReactWrapperComponent.jsx +91 -0
- data/vendor/assets/javascripts/ShallowTraversal.js +173 -0
- data/vendor/assets/javascripts/ShallowWrapper.js +744 -0
- data/vendor/assets/javascripts/Utils.js +266 -0
- data/vendor/assets/javascripts/index.js +14 -0
- data/vendor/assets/javascripts/mount.js +11 -0
- data/vendor/assets/javascripts/react-compat.js +166 -0
- data/vendor/assets/javascripts/render.js +19 -0
- data/vendor/assets/javascripts/shallow.js +11 -0
- data/vendor/assets/javascripts/version.js +5 -0
- metadata +89 -0
@@ -0,0 +1,266 @@
|
|
1
|
+
/* eslint no-use-before-define:0 */
|
2
|
+
import isEqual from 'lodash/isEqual';
|
3
|
+
import {
|
4
|
+
isDOMComponent,
|
5
|
+
findDOMNode,
|
6
|
+
childrenToArray,
|
7
|
+
} from './react-compat';
|
8
|
+
import {
|
9
|
+
REACT013,
|
10
|
+
REACT15,
|
11
|
+
} from './version';
|
12
|
+
|
13
|
+
function internalInstanceKey(node) {
|
14
|
+
return Object.keys(Object(node)).filter(key => key.match(/^__reactInternalInstance\$/))[0];
|
15
|
+
}
|
16
|
+
|
17
|
+
export function internalInstance(inst) {
|
18
|
+
return inst._reactInternalInstance ||
|
19
|
+
inst[internalInstanceKey(inst)];
|
20
|
+
}
|
21
|
+
|
22
|
+
export function isFunctionalComponent(inst) {
|
23
|
+
return inst && inst.constructor && inst.constructor.name === 'StatelessComponent';
|
24
|
+
}
|
25
|
+
|
26
|
+
export function propsOfNode(node) {
|
27
|
+
if (REACT013 && node && node._store) {
|
28
|
+
return (node._store.props) || {};
|
29
|
+
}
|
30
|
+
if (node && node._reactInternalComponent && node._reactInternalComponent._currentElement) {
|
31
|
+
return (node._reactInternalComponent._currentElement.props) || {};
|
32
|
+
}
|
33
|
+
if (node && node._currentElement) {
|
34
|
+
return (node._currentElement.props) || {};
|
35
|
+
}
|
36
|
+
if (REACT15 && node) {
|
37
|
+
if (internalInstance(node) && internalInstance(node)._currentElement) {
|
38
|
+
return (internalInstance(node)._currentElement.props) || {};
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
return (node && node.props) || {};
|
43
|
+
}
|
44
|
+
|
45
|
+
export function typeOfNode(node) {
|
46
|
+
return node ? node.type : null;
|
47
|
+
}
|
48
|
+
|
49
|
+
export function getNode(node) {
|
50
|
+
return isDOMComponent(node) ? findDOMNode(node) : node;
|
51
|
+
}
|
52
|
+
|
53
|
+
export function nodeHasType(node, type) {
|
54
|
+
if (!type || !node) return false;
|
55
|
+
if (!node.type) return false;
|
56
|
+
if (typeof node.type === 'string') return node.type === type;
|
57
|
+
return node.type.name === type || node.type.displayName === type;
|
58
|
+
}
|
59
|
+
|
60
|
+
export function childrenEqual(a, b) {
|
61
|
+
if (a === b) return true;
|
62
|
+
if (!Array.isArray(a) && !Array.isArray(b)) {
|
63
|
+
return nodeEqual(a, b);
|
64
|
+
}
|
65
|
+
if (!a && !b) return true;
|
66
|
+
if (a.length !== b.length) return false;
|
67
|
+
if (a.length === 0 && b.length === 0) return true;
|
68
|
+
for (let i = 0; i < a.length; i++) {
|
69
|
+
if (!nodeEqual(a[i], b[i])) return false;
|
70
|
+
}
|
71
|
+
return true;
|
72
|
+
}
|
73
|
+
|
74
|
+
export function nodeEqual(a, b) {
|
75
|
+
if (a === b) return true;
|
76
|
+
if (!a || !b) return false;
|
77
|
+
if (a.type !== b.type) return false;
|
78
|
+
const left = propsOfNode(a);
|
79
|
+
const leftKeys = Object.keys(left);
|
80
|
+
const right = propsOfNode(b);
|
81
|
+
for (let i = 0; i < leftKeys.length; i++) {
|
82
|
+
const prop = leftKeys[i];
|
83
|
+
if (!(prop in right)) return false;
|
84
|
+
if (prop === 'children') {
|
85
|
+
if (!childrenEqual(childrenToArray(left.children), childrenToArray(right.children))) {
|
86
|
+
return false;
|
87
|
+
}
|
88
|
+
} else if (right[prop] === left[prop]) {
|
89
|
+
// continue;
|
90
|
+
} else if (typeof right[prop] === typeof left[prop] && typeof left[prop] === 'object') {
|
91
|
+
if (!isEqual(left[prop], right[prop])) return false;
|
92
|
+
} else {
|
93
|
+
return false;
|
94
|
+
}
|
95
|
+
}
|
96
|
+
|
97
|
+
if (typeof a !== 'string' && typeof a !== 'number') {
|
98
|
+
return leftKeys.length === Object.keys(right).length;
|
99
|
+
}
|
100
|
+
|
101
|
+
return false;
|
102
|
+
}
|
103
|
+
|
104
|
+
export function containsChildrenSubArray(match, node, subArray) {
|
105
|
+
const children = childrenOfNode(node);
|
106
|
+
const checker = (_, i) => arraysEqual(match, children.slice(i, i + subArray.length), subArray);
|
107
|
+
return children.some(checker);
|
108
|
+
}
|
109
|
+
|
110
|
+
function arraysEqual(match, left, right) {
|
111
|
+
return left.length === right.length && left.every((el, i) => match(el, right[i]));
|
112
|
+
}
|
113
|
+
|
114
|
+
function childrenOfNode(node) {
|
115
|
+
const props = propsOfNode(node);
|
116
|
+
const { children } = props;
|
117
|
+
return childrenToArray(children);
|
118
|
+
}
|
119
|
+
|
120
|
+
|
121
|
+
// 'click' => 'onClick'
|
122
|
+
// 'mouseEnter' => 'onMouseEnter'
|
123
|
+
export function propFromEvent(event) {
|
124
|
+
const nativeEvent = mapNativeEventNames(event);
|
125
|
+
return `on${nativeEvent[0].toUpperCase()}${nativeEvent.substring(1)}`;
|
126
|
+
}
|
127
|
+
|
128
|
+
export function withSetStateAllowed(fn) {
|
129
|
+
// NOTE(lmr):
|
130
|
+
// this is currently here to circumvent a React bug where `setState()` is
|
131
|
+
// not allowed without global being defined.
|
132
|
+
let cleanup = false;
|
133
|
+
if (typeof global.document === 'undefined') {
|
134
|
+
cleanup = true;
|
135
|
+
global.document = {};
|
136
|
+
}
|
137
|
+
fn();
|
138
|
+
if (cleanup) {
|
139
|
+
delete global.document;
|
140
|
+
}
|
141
|
+
}
|
142
|
+
|
143
|
+
export function splitSelector(selector) {
|
144
|
+
return selector.split(/(?=\.|\[.*\])/);
|
145
|
+
}
|
146
|
+
|
147
|
+
export function isSimpleSelector(selector) {
|
148
|
+
// any of these characters pretty much guarantee it's a complex selector
|
149
|
+
return !/[~\s:>]/.test(selector);
|
150
|
+
}
|
151
|
+
|
152
|
+
export function selectorError(selector) {
|
153
|
+
return new TypeError(
|
154
|
+
`Enzyme received a complex CSS selector ('${selector}') that it does not currently support`
|
155
|
+
);
|
156
|
+
}
|
157
|
+
|
158
|
+
export const isCompoundSelector = /([a-z]\.[a-z]|[a-z]\[.*\])/i;
|
159
|
+
|
160
|
+
const isPropSelector = /^\[.*\]$/;
|
161
|
+
|
162
|
+
export const SELECTOR = {
|
163
|
+
CLASS_TYPE: 0,
|
164
|
+
ID_TYPE: 1,
|
165
|
+
PROP_TYPE: 2,
|
166
|
+
};
|
167
|
+
|
168
|
+
export function selectorType(selector) {
|
169
|
+
if (selector[0] === '.') {
|
170
|
+
return SELECTOR.CLASS_TYPE;
|
171
|
+
} else if (selector[0] === '#') {
|
172
|
+
return SELECTOR.ID_TYPE;
|
173
|
+
} else if (isPropSelector.test(selector)) {
|
174
|
+
return SELECTOR.PROP_TYPE;
|
175
|
+
}
|
176
|
+
return undefined;
|
177
|
+
}
|
178
|
+
|
179
|
+
export function AND(fns) {
|
180
|
+
return x => {
|
181
|
+
let i = fns.length;
|
182
|
+
while (i--) {
|
183
|
+
if (!fns[i](x)) return false;
|
184
|
+
}
|
185
|
+
return true;
|
186
|
+
};
|
187
|
+
}
|
188
|
+
|
189
|
+
export function coercePropValue(propName, propValue) {
|
190
|
+
// can be undefined
|
191
|
+
if (propValue === undefined) {
|
192
|
+
return propValue;
|
193
|
+
}
|
194
|
+
|
195
|
+
const trimmedValue = propValue.trim();
|
196
|
+
|
197
|
+
// if propValue includes quotes, it should be
|
198
|
+
// treated as a string
|
199
|
+
if (/^(['"]).*\1$/.test(trimmedValue)) {
|
200
|
+
return trimmedValue.slice(1, -1);
|
201
|
+
}
|
202
|
+
|
203
|
+
const numericPropValue = +trimmedValue;
|
204
|
+
|
205
|
+
// if parseInt is not NaN, then we've wanted a number
|
206
|
+
if (!isNaN(numericPropValue)) {
|
207
|
+
return numericPropValue;
|
208
|
+
}
|
209
|
+
|
210
|
+
// coerce to boolean
|
211
|
+
if (trimmedValue === 'true') return true;
|
212
|
+
if (trimmedValue === 'false') return false;
|
213
|
+
|
214
|
+
// user provided an unquoted string value
|
215
|
+
throw new TypeError(
|
216
|
+
`Enzyme::Unable to parse selector '[${propName}=${propValue}]'. ` +
|
217
|
+
`Perhaps you forgot to escape a string? Try '[${propName}="${trimmedValue}"]' instead.`
|
218
|
+
);
|
219
|
+
}
|
220
|
+
|
221
|
+
export function mapNativeEventNames(event) {
|
222
|
+
const nativeToReactEventMap = {
|
223
|
+
compositionend: 'compositionEnd',
|
224
|
+
compositionstart: 'compositionStart',
|
225
|
+
compositionupdate: 'compositionUpdate',
|
226
|
+
keydown: 'keyDown',
|
227
|
+
keyup: 'keyUp',
|
228
|
+
keypress: 'keyPress',
|
229
|
+
contextmenu: 'contextMenu',
|
230
|
+
dblclick: 'doubleClick',
|
231
|
+
doubleclick: 'doubleClick', // kept for legacy. TODO: remove with next major.
|
232
|
+
dragend: 'dragEnd',
|
233
|
+
dragenter: 'dragEnter',
|
234
|
+
dragexist: 'dragExit',
|
235
|
+
dragleave: 'dragLeave',
|
236
|
+
dragover: 'dragOver',
|
237
|
+
dragstart: 'dragStart',
|
238
|
+
mousedown: 'mouseDown',
|
239
|
+
mousemove: 'mouseMove',
|
240
|
+
mouseout: 'mouseOut',
|
241
|
+
mouseover: 'mouseOver',
|
242
|
+
mouseup: 'mouseUp',
|
243
|
+
touchcancel: 'touchCancel',
|
244
|
+
touchend: 'touchEnd',
|
245
|
+
touchmove: 'touchMove',
|
246
|
+
touchstart: 'touchStart',
|
247
|
+
canplay: 'canPlay',
|
248
|
+
canplaythrough: 'canPlayThrough',
|
249
|
+
durationchange: 'durationChange',
|
250
|
+
loadeddata: 'loadedData',
|
251
|
+
loadedmetadata: 'loadedMetadata',
|
252
|
+
loadstart: 'loadStart',
|
253
|
+
ratechange: 'rateChange',
|
254
|
+
timeupdate: 'timeUpdate',
|
255
|
+
volumechange: 'volumeChange',
|
256
|
+
};
|
257
|
+
|
258
|
+
if (!REACT013) {
|
259
|
+
// these could not be simulated in React 0.13:
|
260
|
+
// https://github.com/facebook/react/issues/1297
|
261
|
+
nativeToReactEventMap.mouseenter = 'mouseEnter';
|
262
|
+
nativeToReactEventMap.mouseleave = 'mouseLeave';
|
263
|
+
}
|
264
|
+
|
265
|
+
return nativeToReactEventMap[event] || event;
|
266
|
+
}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import ReactWrapper from './ReactWrapper';
|
2
|
+
import ShallowWrapper from './ShallowWrapper';
|
3
|
+
|
4
|
+
import mount from './mount';
|
5
|
+
import shallow from './shallow';
|
6
|
+
import render from './render';
|
7
|
+
|
8
|
+
export {
|
9
|
+
render,
|
10
|
+
shallow,
|
11
|
+
mount,
|
12
|
+
ShallowWrapper,
|
13
|
+
ReactWrapper,
|
14
|
+
};
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import ReactWrapper from './ReactWrapper';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Mounts and renders a react component into the document and provides a testing wrapper around it.
|
5
|
+
*
|
6
|
+
* @param node
|
7
|
+
* @returns {ReactWrapper}
|
8
|
+
*/
|
9
|
+
export default function mount(node, options) {
|
10
|
+
return new ReactWrapper(node, null, options);
|
11
|
+
}
|
@@ -0,0 +1,166 @@
|
|
1
|
+
/* eslint react/no-deprecated: 0 */
|
2
|
+
import { REACT013 } from './version';
|
3
|
+
import objectAssign from 'object.assign';
|
4
|
+
|
5
|
+
let TestUtils;
|
6
|
+
let createShallowRenderer;
|
7
|
+
let renderToStaticMarkup;
|
8
|
+
let renderIntoDocument;
|
9
|
+
let findDOMNode;
|
10
|
+
let childrenToArray;
|
11
|
+
let renderWithOptions;
|
12
|
+
let unmountComponentAtNode;
|
13
|
+
|
14
|
+
const React = require('react');
|
15
|
+
|
16
|
+
if (REACT013) {
|
17
|
+
renderToStaticMarkup = React.renderToStaticMarkup;
|
18
|
+
/* eslint-disable react/no-deprecated */
|
19
|
+
findDOMNode = React.findDOMNode;
|
20
|
+
unmountComponentAtNode = React.unmountComponentAtNode;
|
21
|
+
/* eslint-enable react/no-deprecated */
|
22
|
+
TestUtils = require('react/addons').addons.TestUtils;
|
23
|
+
const ReactContext = require('react/lib/ReactContext');
|
24
|
+
|
25
|
+
// Shallow rendering in 0.13 did not properly support context. This function provides a shim
|
26
|
+
// around `TestUtils.createRenderer` that instead returns a ShallowRenderer that actually
|
27
|
+
// works with context. See https://github.com/facebook/react/issues/3721 for more details.
|
28
|
+
createShallowRenderer = function createRendererCompatible() {
|
29
|
+
const renderer = TestUtils.createRenderer();
|
30
|
+
renderer.render = (originalRender => function contextCompatibleRender(node, context = {}) {
|
31
|
+
ReactContext.current = context;
|
32
|
+
originalRender.call(this, React.createElement(node.type, node.props), context);
|
33
|
+
ReactContext.current = {};
|
34
|
+
return renderer.getRenderOutput();
|
35
|
+
})(renderer.render);
|
36
|
+
return renderer;
|
37
|
+
};
|
38
|
+
renderIntoDocument = TestUtils.renderIntoDocument;
|
39
|
+
// this fixes some issues in React 0.13 with setState and jsdom...
|
40
|
+
// see issue: https://github.com/airbnb/enzyme/issues/27
|
41
|
+
require('react/lib/ExecutionEnvironment').canUseDOM = true;
|
42
|
+
|
43
|
+
// in 0.13, a Children.toArray function was not exported. Make our own instead.
|
44
|
+
childrenToArray = (children) => {
|
45
|
+
const results = [];
|
46
|
+
if (children !== undefined && children !== null && children !== false) {
|
47
|
+
React.Children.forEach(children, (el) => {
|
48
|
+
if (el !== undefined && el !== null && el !== false) {
|
49
|
+
results.push(el);
|
50
|
+
}
|
51
|
+
});
|
52
|
+
}
|
53
|
+
return results;
|
54
|
+
};
|
55
|
+
|
56
|
+
renderWithOptions = (node, options) => {
|
57
|
+
if (options.attachTo) {
|
58
|
+
return React.render(node, options.attachTo);
|
59
|
+
}
|
60
|
+
return TestUtils.renderIntoDocument(node);
|
61
|
+
};
|
62
|
+
} else {
|
63
|
+
let ReactDOM;
|
64
|
+
|
65
|
+
try {
|
66
|
+
ReactDOM = require('react-dom');
|
67
|
+
} catch (e) {
|
68
|
+
console.error(
|
69
|
+
'react-dom is an implicit dependency in order to support react@0.13-14. ' +
|
70
|
+
'Please add the appropriate version to your devDependencies. ' +
|
71
|
+
'See https://github.com/airbnb/enzyme#installation'
|
72
|
+
);
|
73
|
+
throw e;
|
74
|
+
}
|
75
|
+
|
76
|
+
renderToStaticMarkup = require('react-dom/server').renderToStaticMarkup;
|
77
|
+
findDOMNode = ReactDOM.findDOMNode;
|
78
|
+
unmountComponentAtNode = ReactDOM.unmountComponentAtNode;
|
79
|
+
// We require the testutils, but they don't come with 0.14 out of the box, so we
|
80
|
+
// require them here through this node module. The bummer is that we are not able
|
81
|
+
// to list this as a dependency in package.json and have 0.13 work properly.
|
82
|
+
// As a result, right now this is basically an implicit dependency.
|
83
|
+
try {
|
84
|
+
TestUtils = require('react-addons-test-utils');
|
85
|
+
} catch (e) {
|
86
|
+
console.error(
|
87
|
+
'react-addons-test-utils is an implicit dependency in order to support react@0.13-14. ' +
|
88
|
+
'Please add the appropriate version to your devDependencies. ' +
|
89
|
+
'See https://github.com/airbnb/enzyme#installation'
|
90
|
+
);
|
91
|
+
throw e;
|
92
|
+
}
|
93
|
+
|
94
|
+
// Shallow rendering changed from 0.13 => 0.14 in such a way that
|
95
|
+
// 0.14 now does not allow shallow rendering of native DOM elements.
|
96
|
+
// This is mainly because the result of such a call should not realistically
|
97
|
+
// be any different than the JSX you passed in (result of `React.createElement`.
|
98
|
+
// In order to maintain the same behavior across versions, this function
|
99
|
+
// is essentially a replacement for `TestUtils.createRenderer` that doesn't use
|
100
|
+
// shallow rendering when it's just a DOM element.
|
101
|
+
createShallowRenderer = function createRendererCompatible() {
|
102
|
+
const renderer = TestUtils.createRenderer();
|
103
|
+
const originalRender = renderer.render;
|
104
|
+
const originalRenderOutput = renderer.getRenderOutput;
|
105
|
+
let isDOM = false;
|
106
|
+
let _node;
|
107
|
+
return objectAssign(renderer, {
|
108
|
+
render(node, context) {
|
109
|
+
/* eslint consistent-return: 0 */
|
110
|
+
if (typeof node.type === 'string') {
|
111
|
+
isDOM = true;
|
112
|
+
_node = node;
|
113
|
+
} else {
|
114
|
+
isDOM = false;
|
115
|
+
return originalRender.call(this, node, context);
|
116
|
+
}
|
117
|
+
},
|
118
|
+
getRenderOutput() {
|
119
|
+
if (isDOM) {
|
120
|
+
return _node;
|
121
|
+
}
|
122
|
+
return originalRenderOutput.call(this);
|
123
|
+
},
|
124
|
+
});
|
125
|
+
};
|
126
|
+
renderIntoDocument = TestUtils.renderIntoDocument;
|
127
|
+
childrenToArray = React.Children.toArray;
|
128
|
+
|
129
|
+
renderWithOptions = (node, options) => {
|
130
|
+
if (options.attachTo) {
|
131
|
+
return ReactDOM.render(node, options.attachTo);
|
132
|
+
}
|
133
|
+
return TestUtils.renderIntoDocument(node);
|
134
|
+
};
|
135
|
+
}
|
136
|
+
|
137
|
+
const {
|
138
|
+
mockComponent,
|
139
|
+
isElement,
|
140
|
+
isElementOfType,
|
141
|
+
isDOMComponent,
|
142
|
+
isCompositeComponent,
|
143
|
+
isCompositeComponentWithType,
|
144
|
+
isCompositeComponentElement,
|
145
|
+
Simulate,
|
146
|
+
findAllInRenderedTree,
|
147
|
+
} = TestUtils;
|
148
|
+
|
149
|
+
export {
|
150
|
+
createShallowRenderer,
|
151
|
+
renderToStaticMarkup,
|
152
|
+
renderIntoDocument,
|
153
|
+
mockComponent,
|
154
|
+
isElement,
|
155
|
+
isElementOfType,
|
156
|
+
isDOMComponent,
|
157
|
+
isCompositeComponent,
|
158
|
+
isCompositeComponentWithType,
|
159
|
+
isCompositeComponentElement,
|
160
|
+
Simulate,
|
161
|
+
findDOMNode,
|
162
|
+
findAllInRenderedTree,
|
163
|
+
childrenToArray,
|
164
|
+
renderWithOptions,
|
165
|
+
unmountComponentAtNode,
|
166
|
+
};
|