-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathindex.js
349 lines (296 loc) · 9.35 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
'use strict';
var toString = require('react-element-to-jsx-string');
var beautify = require('html/lib/html');
var cheerio = require('cheerio');
var enzyme = require('enzyme');
//
// The `react-element-to-jsx-string` is converted from an ES6 module to ES5 so
// it has a weird .default syntax.
//
toString = toString.default || toString;
/**
* Expose the Assume plugin interface.
*
* @param {Assume} assume The assume instance.
* @param {Object} util Utilities provided by assume.
* @public
*/
module.exports = function plugin(assume, util) {
var hasOwn = Object.prototype.hasOwnProperty;
var ShallowWrapper = enzyme.ShallowWrapper;
var ReactWrapper = enzyme.ReactWrapper;
var format = util.format;
//
// Introduce a new flag.
//
assume.flags._anywhere = 'anywhere, somewhere';
/**
* Helper function to check if a given value is an enzyme instance.
*
* @param {Enzyme} value Possible wrapper.
* @returns {Boolean} If it's a wrapper.
* @private
*/
function isEnzyme(value) {
return value instanceof ShallowWrapper || value instanceof ReactWrapper;
}
/**
* Transform a wrapper to HTML we could walk through.
*
* @param {Enzyme} value Possible wrapper.
* @returns {Cheerio} Transformed HTML.
* @private
*/
function html(value) {
return cheerio(value.html());
}
/**
* Transform a wrapper of children in to a proper array.
*
* @param {Enzyme} wrapper Wrapper
* @private
*/
function toArray(wrapper) {
var result = new Array(wrapper.length);
wrapper.forEach(function each(node, index){
result[index] = node;
});
return result;
}
/**
* Clean up the HTML like output of the enzyme debug command so it's more
* human readable in assertion messages.
*
* @param {Enzyme} value Enzyme instance
* @returns {String} Component.
* @private
*/
function debug(value) {
return beautify.prettyPrint(value.debug(), {
indent_size: 2
});
}
/**
* Check if anything in the tree matches this.
*
* @param {Enzyme} value Wrapper we want to iterate over.
* @param {Function} fn Iterator.
* @returns {Boolean} Did we found anything that matched.
* @private
*/
function anywhere(value, fn) {
var children = toArray(value.children());
var found = false;
while (children.length) {
var node = children.shift();
found = fn(node);
if (found) break;
Array.prototype.push.apply(children, toArray(node.children()));
}
return found;
}
/**
* Check if an object has a given set of keys / values.
*
* @param {Object} value Object that needs property checking
* @param {Array|Object} what keys or object that it should include.
* @private
*/
function properties(value, what) {
var passed = true;
if (util.type(what) === 'array') {
util.each(what, function each(key) {
passed = hasOwn.call(value, key);
if (!passed) return false;
});
} else {
var keys = [];
for (var key in what) {
keys.push(key);
}
util.each(keys, function each(key) {
passed = util.deep(value[key], what[key]);
if (!passed) return false;
});
}
return passed;
}
/**
* Assert that our given value is an enzyme wrapper.
*
* @param {String} msg Reason of assertion failure.
* @returns {Assume} The assume instance for chaining.
* @public
*/
assume.add('enzyme', function enzymes(msg) {
var value = this.value
, expect = format('%j to @ be an enzyme instance', value);
return this.test(isEnzyme(value), msg, expect);
});
/**
* Assert if the wrapper has a given className.
*
* @param {String} str Name of the className it should contain.
* @param {String} msg Reason of assertion failure.
* @returns {Assume} The assume instance for chaining.
* @public
*/
assume.add('className, classNames', function className(str, msg) {
var value = this.value
, found = value.hasClass(str)
, expect = format('`%s` to @ have class %s', value.props().className, str);
if (!this._anywhere || found) return this.test(found, msg, expect);
found = anywhere(value, function iterate(node) {
return node.hasClass(str);
});
return this.test(found, msg, expect);
});
/**
* Assert if the wrapper contains a given component/node.
*
* @param {String} component The component or node it should have.
* @param {String} msg Reason of assertion failure.
* @returns {Assume} The assume instance for chaining.
* @public
*/
assume.add('contain, contains', function contain(component, msg) {
if (!isEnzyme(this.value)) {
return this.clone(this.value).includes(component, msg);
}
var value = this.value
, found = value.contains(component)
, expect = format('%s to @ contain %s', debug(value), toString(component));
if (!this._anywhere || found) return this.test(found, msg, expect);
found = anywhere(value, function iterate(node) {
return node.contains(component);
});
return this.test(found, msg, expect);
});
/**
* Assert that an element has a given tag name.
*
* @param {String} name Tag name it should have.
* @param {String} msg Reason of assertion failure.
* @returns {Assume} The assume instance for chaining.
* @public
*/
assume.add('tagName', function tagName(name, msg) {
var value = html(this.value)[0].name
, expect = format('%s to @ have tag name %s', value, name);
return this.test(value === name, msg, expect);
});
/**
* Assert that the given component is checked.
*
* @param {String} msg Reason of assertion failure.
* @returns {Assume} The assume instance for chaining.
* @public
*/
assume.add('checked', function checked(msg) {
var value = html(this.value).is(':checked')
, expect = format('%s component to @ be checked', debug(this.value));
return this.test(value, msg, expect);
});
/**
* Assert that the given component is disabled.
*
* @param {String} msg Reason of assertion failure.
* @returns {Assume} The assume instance for chaining.
* @public
*/
assume.add('disabled', function disabled(msg) {
var value = html(this.value).is(':disabled')
, expect = format('%s component to @ be disabled', debug(this.value));
return this.test(value, msg, expect);
});
/**
* Assert that the given component has a ref with the given name.
*
* @param {String} name Name of the reference that should be on the component.
* @param {String} msg Reason of assertion failure.
* @returns {Assume} The assume instance for chaining.
* @public
*/
assume.add('ref', function ref(name, msg) {
var value = (this.value.instance().refs || {})[name]
, expect = format('%s component to @ ref %s', debug(this.value), name);
return this.test(!!value, msg, expect);
});
/**
* Assert props.
*
* @param {Array|Object} what Keys, or key/value we want to include.
* @param {String} msg Reason of assertion failure.
* @returns {Assume} The assume instance for chaining.
* @public
*/
assume.add('props', function props(what, msg) {
var value = this.value
, expect
, passed;
//
// Hack: When calling .props() on a root node, it doesn't return the props.
// So we need to check if we need to get the instance props or enzyme's
// props.
//
if (value.root == value) {
value = value.instance().props;
} else {
value = value.props();
}
expect = format('%j to @ include props %j', value, what);
passed = properties(value, what);
return this.test(passed, msg, expect);
});
/**
* Assert state.
*
* @param {Array|Object} what Keys, or key/value we want to include.
* @param {String} msg Reason of assertion failure.
* @returns {Assume} The assume instance for chaining.
* @public
*/
assume.add('state', function state(what, msg) {
var value = this.value.state()
, expect = format('%j to @ include state %j', value, what)
, passed = properties(value, what);
return this.test(passed, msg, expect);
});
/**
* Assert that the HTML output of a component includes a given string.
*
* @param {String} str HTML string the representation should include.
* @param {String} msg Reason of assertion failure.
* @returns {Assume} The assume instance for chaining.
* @public
*/
assume.add('html', function htmls(str, msg) {
var outer = value.html().replace(/\sdata-reactid+="[^"]+"/g, '');
return this.clone(value).includes(str, msg);
});
/**
* Assert that a component or child is empty.
*
* @param {String} msg Reason of assertion failure.
* @returns {Assume} The assume instance for chaining.
* @public
*/
assume.add('blank, empty', function blank(msg) {
var value = this.value
, expect = format('%s to @ be empty', debug(value));
return this.test(value.children().length === 0, msg, expect);
});
/**
* Assert that a given wrapper has the component name.
*
* @param {String} what Name of the component.
* @param {String} msg Reason of assertion failure.
* @returns {Assume} The assume instance for chaining.
* @public
*/
assume.add('name', function type(what, msg) {
var value = this.value.name()
, expect = format('Component name %s to be type %s', value, what);
return this.test(value === what, msg, expect);
});
};