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.
@@ -0,0 +1,777 @@
1
+ import React from 'react';
2
+ import cheerio from 'cheerio';
3
+ import flatten from 'lodash/flatten';
4
+ import unique from 'lodash/uniq';
5
+ import compact from 'lodash/compact';
6
+ import createWrapperComponent from './ReactWrapperComponent';
7
+ import {
8
+ instHasClassName,
9
+ childrenOfInst,
10
+ parentsOfInst,
11
+ buildInstPredicate,
12
+ instEqual,
13
+ treeFilter,
14
+ getNode,
15
+ } from './MountedTraversal';
16
+ import {
17
+ renderWithOptions,
18
+ Simulate,
19
+ findDOMNode,
20
+ unmountComponentAtNode,
21
+ } from './react-compat';
22
+ import {
23
+ mapNativeEventNames,
24
+ containsChildrenSubArray,
25
+ propsOfNode,
26
+ typeOfNode,
27
+ } from './Utils';
28
+ import {
29
+ debugInsts,
30
+ } from './Debug';
31
+
32
+ /**
33
+ * Finds all nodes in the current wrapper nodes' render trees that match the provided predicate
34
+ * function.
35
+ *
36
+ * @param {ReactWrapper} wrapper
37
+ * @param {Function} predicate
38
+ * @returns {ReactWrapper}
39
+ */
40
+ function findWhereUnwrapped(wrapper, predicate) {
41
+ return wrapper.flatMap(n => treeFilter(n.node, predicate));
42
+ }
43
+
44
+ /**
45
+ * Returns a new wrapper instance with only the nodes of the current wrapper instance that match
46
+ * the provided predicate function.
47
+ *
48
+ * @param {ReactWrapper} wrapper
49
+ * @param {Function} predicate
50
+ * @returns {ReactWrapper}
51
+ */
52
+ function filterWhereUnwrapped(wrapper, predicate) {
53
+ return wrapper.wrap(compact(wrapper.nodes.filter(predicate)));
54
+ }
55
+
56
+ /**
57
+ * @class ReactWrapper
58
+ */
59
+ export default class ReactWrapper {
60
+
61
+ constructor(nodes, root, options = {}) {
62
+ if (!global.window && !global.document) {
63
+ throw new Error(
64
+ 'It looks like you called `mount()` without a global document being loaded.'
65
+ );
66
+ }
67
+
68
+ if (!root) {
69
+ const ReactWrapperComponent = createWrapperComponent(nodes, options);
70
+ this.component = renderWithOptions(
71
+ <ReactWrapperComponent
72
+ Component={nodes.type}
73
+ props={nodes.props}
74
+ context={options.context}
75
+ />,
76
+ options);
77
+ this.root = this;
78
+ this.node = this.component.getWrappedComponent();
79
+ this.nodes = [this.node];
80
+ this.length = 1;
81
+ } else {
82
+ this.component = null;
83
+ this.root = root;
84
+ if (!Array.isArray(nodes)) {
85
+ this.node = nodes;
86
+ this.nodes = [nodes];
87
+ } else {
88
+ this.node = nodes[0];
89
+ this.nodes = nodes;
90
+ }
91
+ this.length = this.nodes.length;
92
+ }
93
+ this.options = options;
94
+ }
95
+
96
+ /**
97
+ * If the root component contained a ref, you can access it here
98
+ * and get a wrapper around it.
99
+ *
100
+ * NOTE: can only be called on a wrapper instance that is also the root instance.
101
+ *
102
+ * @param {String} refname
103
+ * @returns {ReactWrapper}
104
+ */
105
+ ref(refname) {
106
+ if (this.root !== this) {
107
+ throw new Error('ReactWrapper::ref(refname) can only be called on the root');
108
+ }
109
+ return this.wrap(this.instance().refs[refname]);
110
+ }
111
+
112
+ /**
113
+ * Gets the instance of the component being rendered as the root node passed into `mount()`.
114
+ *
115
+ * NOTE: can only be called on a wrapper instance that is also the root instance.
116
+ *
117
+ * Example:
118
+ * ```
119
+ * const wrapper = mount(<MyComponent />);
120
+ * const inst = wrapper.instance();
121
+ * expect(inst).to.be.instanceOf(MyComponent);
122
+ * ```
123
+ * @returns {ReactComponent}
124
+ */
125
+ instance() {
126
+ if (this.root !== this) {
127
+ throw new Error('ReactWrapper::instance() can only be called on the root');
128
+ }
129
+ return this.component.getInstance();
130
+ }
131
+
132
+ /**
133
+ * Forces a re-render. Useful to run before checking the render output if something external
134
+ * may be updating the state of the component somewhere.
135
+ *
136
+ * NOTE: can only be called on a wrapper instance that is also the root instance.
137
+ *
138
+ * @returns {ReactWrapper}
139
+ */
140
+ update() {
141
+ if (this.root !== this) {
142
+ // TODO(lmr): this requirement may not be necessary for the ReactWrapper
143
+ throw new Error('ReactWrapper::update() can only be called on the root');
144
+ }
145
+ this.single(() => {
146
+ this.component.forceUpdate();
147
+ });
148
+ return this;
149
+ }
150
+
151
+ /**
152
+ * A method that unmounts the component. This can be used to simulate a component going through
153
+ * and unmount/mount lifecycle.
154
+ *
155
+ * @returns {ReactWrapper}
156
+ */
157
+ unmount() {
158
+ if (this.root !== this) {
159
+ throw new Error('ReactWrapper::unmount() can only be called on the root');
160
+ }
161
+ this.single(() => {
162
+ this.component.setState({ mount: false });
163
+ });
164
+ return this;
165
+ }
166
+
167
+ /**
168
+ * A method that re-mounts the component. This can be used to simulate a component going through
169
+ * an unmount/mount lifecycle.
170
+ *
171
+ * @returns {ReactWrapper}
172
+ */
173
+ mount() {
174
+ if (this.root !== this) {
175
+ throw new Error('ReactWrapper::mount() can only be called on the root');
176
+ }
177
+ this.single(() => {
178
+ this.component.setState({ mount: true });
179
+ });
180
+ return this;
181
+ }
182
+
183
+ /**
184
+ * A method that sets the props of the root component, and re-renders. Useful for when you are
185
+ * wanting to test how the component behaves over time with changing props. Calling this, for
186
+ * instance, will call the `componentWillReceiveProps` lifecycle method.
187
+ *
188
+ * Similar to `setState`, this method accepts a props object and will merge it in with the already
189
+ * existing props.
190
+ *
191
+ * NOTE: can only be called on a wrapper instance that is also the root instance.
192
+ *
193
+ * @param {Object} props object
194
+ * @returns {ReactWrapper}
195
+ */
196
+ setProps(props) {
197
+ if (this.root !== this) {
198
+ throw new Error('ReactWrapper::setProps() can only be called on the root');
199
+ }
200
+ this.component.setChildProps(props);
201
+ return this;
202
+ }
203
+
204
+ /**
205
+ * A method to invoke `setState` on the root component instance similar to how you might in the
206
+ * definition of the component, and re-renders. This method is useful for testing your component
207
+ * in hard to achieve states, however should be used sparingly. If possible, you should utilize
208
+ * your component's external API in order to get it into whatever state you want to test, in order
209
+ * to be as accurate of a test as possible. This is not always practical, however.
210
+ *
211
+ * NOTE: can only be called on a wrapper instance that is also the root instance.
212
+ *
213
+ * @param {Object} state to merge
214
+ * @returns {ReactWrapper}
215
+ */
216
+ setState(state) {
217
+ if (this.root !== this) {
218
+ throw new Error('ReactWrapper::setState() can only be called on the root');
219
+ }
220
+ this.instance().setState(state);
221
+ return this;
222
+ }
223
+
224
+ /**
225
+ * A method that sets the context of the root component, and re-renders. Useful for when you are
226
+ * wanting to test how the component behaves over time with changing contexts.
227
+ *
228
+ * NOTE: can only be called on a wrapper instance that is also the root instance.
229
+ *
230
+ * @param {Object} context object
231
+ * @returns {ReactWrapper}
232
+ */
233
+ setContext(context) {
234
+ if (this.root !== this) {
235
+ throw new Error('ReactWrapper::setContext() can only be called on the root');
236
+ }
237
+ if (!this.options.context) {
238
+ throw new Error(
239
+ 'ShallowWrapper::setContext() can only be called on a wrapper that was originally passed ' +
240
+ 'a context option'
241
+ );
242
+ }
243
+ this.component.setChildContext(context);
244
+ return this;
245
+ }
246
+
247
+ /**
248
+ * Whether or not a given react element exists in the mount render tree.
249
+ *
250
+ * Example:
251
+ * ```
252
+ * const wrapper = mount(<MyComponent />);
253
+ * expect(wrapper.contains(<div className="foo bar" />)).to.equal(true);
254
+ * ```
255
+ *
256
+ * @param {ReactElement|Array<ReactElement>} nodeOrNodes
257
+ * @returns {Boolean}
258
+ */
259
+ contains(nodeOrNodes) {
260
+ const predicate = Array.isArray(nodeOrNodes)
261
+ ? other => containsChildrenSubArray(instEqual, other, nodeOrNodes)
262
+ : other => instEqual(nodeOrNodes, other);
263
+ return findWhereUnwrapped(this, predicate).length > 0;
264
+ }
265
+
266
+ /**
267
+ * Finds every node in the render tree of the current wrapper that matches the provided selector.
268
+ *
269
+ * @param {String|Function} selector
270
+ * @returns {ReactWrapper}
271
+ */
272
+ find(selector) {
273
+ const predicate = buildInstPredicate(selector);
274
+ return findWhereUnwrapped(this, predicate);
275
+ }
276
+
277
+ /**
278
+ * Returns whether or not current node matches a provided selector.
279
+ *
280
+ * NOTE: can only be called on a wrapper of a single node.
281
+ *
282
+ * @param {String|Function} selector
283
+ * @returns {boolean}
284
+ */
285
+ is(selector) {
286
+ const predicate = buildInstPredicate(selector);
287
+ return this.single(n => predicate(n));
288
+ }
289
+
290
+ /**
291
+ * Returns a new wrapper instance with only the nodes of the current wrapper instance that match
292
+ * the provided predicate function.
293
+ *
294
+ * @param {Function} predicate
295
+ * @returns {ReactWrapper}
296
+ */
297
+ filterWhere(predicate) {
298
+ return filterWhereUnwrapped(this, n => predicate(this.wrap(n)));
299
+ }
300
+
301
+ /**
302
+ * Returns a new wrapper instance with only the nodes of the current wrapper instance that match
303
+ * the provided selector.
304
+ *
305
+ * @param {String|Function} selector
306
+ * @returns {ReactWrapper}
307
+ */
308
+ filter(selector) {
309
+ const predicate = buildInstPredicate(selector);
310
+ return filterWhereUnwrapped(this, predicate);
311
+ }
312
+
313
+ /**
314
+ * Returns a new wrapper instance with only the nodes of the current wrapper that did not match
315
+ * the provided selector. Essentially the inverse of `filter`.
316
+ *
317
+ * @param {String|Function} selector
318
+ * @returns {ReactWrapper}
319
+ */
320
+ not(selector) {
321
+ const predicate = buildInstPredicate(selector);
322
+ return filterWhereUnwrapped(this, n => !predicate(n));
323
+ }
324
+
325
+ /**
326
+ * Returns a string of the rendered text of the current render tree. This function should be
327
+ * looked at with skepticism if being used to test what the actual HTML output of the component
328
+ * will be. If that is what you would like to test, use enzyme's `render` function instead.
329
+ *
330
+ * NOTE: can only be called on a wrapper of a single node.
331
+ *
332
+ * @returns {String}
333
+ */
334
+ text() {
335
+ return this.single(n => findDOMNode(n).textContent);
336
+ }
337
+
338
+ /**
339
+ * Returns the HTML of the node.
340
+ *
341
+ * NOTE: can only be called on a wrapper of a single node.
342
+ *
343
+ * @returns {String}
344
+ */
345
+ html() {
346
+ return this.single(n => {
347
+ const node = findDOMNode(n);
348
+ return node === null ? null :
349
+ node.outerHTML.replace(/\sdata-(reactid|reactroot)+="([^"]*)+"/g, '');
350
+ });
351
+ }
352
+
353
+ /**
354
+ * Returns the current node rendered to HTML and wrapped in a CheerioWrapper.
355
+ *
356
+ * NOTE: can only be called on a wrapper of a single node.
357
+ *
358
+ * @returns {CheerioWrapper}
359
+ */
360
+ render() {
361
+ const html = this.html();
362
+ return html === null ? cheerio() : cheerio.load(html).root();
363
+ }
364
+
365
+ /**
366
+ * Used to simulate events. Pass an eventname and (optionally) event arguments. This method of
367
+ * testing events should be met with some skepticism.
368
+ *
369
+ * @param {String} event
370
+ * @param {Array} args
371
+ * @returns {ReactWrapper}
372
+ */
373
+ simulate(event, ...args) {
374
+ this.single(n => {
375
+ const mappedEvent = mapNativeEventNames(event);
376
+ const eventFn = Simulate[mappedEvent];
377
+ if (!eventFn) {
378
+ throw new TypeError(`ReactWrapper::simulate() event '${event}' does not exist`);
379
+ }
380
+
381
+ eventFn(findDOMNode(n), ...args);
382
+ });
383
+ return this;
384
+ }
385
+
386
+ /**
387
+ * Returns the props hash for the root node of the wrapper.
388
+ *
389
+ * NOTE: can only be called on a wrapper of a single node.
390
+ *
391
+ * @returns {Object}
392
+ */
393
+ props() {
394
+ return this.single(propsOfNode);
395
+ }
396
+
397
+ /**
398
+ * Returns the state hash for the root node of the wrapper. Optionally pass in a prop name and it
399
+ * will return just that value.
400
+ *
401
+ * NOTE: can only be called on a wrapper of a single node.
402
+ *
403
+ * @param {String} name (optional)
404
+ * @returns {*}
405
+ */
406
+ state(name) {
407
+ if (this.root !== this) {
408
+ throw new Error('ReactWrapper::state() can only be called on the root');
409
+ }
410
+ const _state = this.single(() => this.instance().state);
411
+ if (name !== undefined) {
412
+ return _state[name];
413
+ }
414
+ return _state;
415
+ }
416
+
417
+ /**
418
+ * Returns the context hash for the root node of the wrapper.
419
+ * Optionally pass in a prop name and it will return just that value.
420
+ *
421
+ * NOTE: can only be called on a wrapper of a single node.
422
+ *
423
+ * @param {String} name (optional)
424
+ * @returns {*}
425
+ */
426
+ context(name) {
427
+ if (this.root !== this) {
428
+ throw new Error('ReactWrapper::context() can only be called on the root');
429
+ }
430
+ const _context = this.single(() => this.instance().context);
431
+ if (name !== undefined) {
432
+ return _context[name];
433
+ }
434
+ return _context;
435
+ }
436
+
437
+ /**
438
+ * Returns a new wrapper with all of the children of the current wrapper.
439
+ *
440
+ * @param {String|Function} [selector]
441
+ * @returns {ReactWrapper}
442
+ */
443
+ children(selector) {
444
+ const allChildren = this.flatMap(n => childrenOfInst(n.node));
445
+ return selector ? allChildren.filter(selector) : allChildren;
446
+ }
447
+
448
+ /**
449
+ * Returns a new wrapper with a specific child
450
+ *
451
+ * @param {Number} [index]
452
+ * @returns {ReactWrapper}
453
+ */
454
+ childAt(index) {
455
+ return this.single(() => this.children().at(index));
456
+ }
457
+
458
+ /**
459
+ * Returns a wrapper around all of the parents/ancestors of the wrapper. Does not include the node
460
+ * in the current wrapper.
461
+ *
462
+ * NOTE: can only be called on a wrapper of a single node.
463
+ *
464
+ * @param {String|Function} [selector]
465
+ * @returns {ReactWrapper}
466
+ */
467
+ parents(selector) {
468
+ const allParents = this.wrap(this.single(n => parentsOfInst(n, this.root.node)));
469
+ return selector ? allParents.filter(selector) : allParents;
470
+ }
471
+
472
+ /**
473
+ * Returns a wrapper around the immediate parent of the current node.
474
+ *
475
+ * @returns {ReactWrapper}
476
+ */
477
+ parent() {
478
+ return this.flatMap(n => [n.parents().get(0)]);
479
+ }
480
+
481
+ /**
482
+ *
483
+ * @param {String|Function} selector
484
+ * @returns {ReactWrapper}
485
+ */
486
+ closest(selector) {
487
+ return this.is(selector) ? this : this.parents().filter(selector).first();
488
+ }
489
+
490
+ /**
491
+ * Returns the value of prop with the given name of the root node.
492
+ *
493
+ * @param {String} propName
494
+ * @returns {*}
495
+ */
496
+ prop(propName) {
497
+ return this.props()[propName];
498
+ }
499
+
500
+ /**
501
+ * Returns the key assigned to the current node.
502
+ *
503
+ * @returns {String}
504
+ */
505
+ key() {
506
+ return this.single((n) => getNode(n).key);
507
+ }
508
+
509
+ /**
510
+ * Returns the type of the root node of this wrapper. If it's a composite component, this will be
511
+ * the component constructor. If it's native DOM node, it will be a string.
512
+ *
513
+ * @returns {String|Function}
514
+ */
515
+ type() {
516
+ return this.single(n => typeOfNode(getNode(n)));
517
+ }
518
+
519
+ /**
520
+ * Returns whether or not the current root node has the given class name or not.
521
+ *
522
+ * NOTE: can only be called on a wrapper of a single node.
523
+ *
524
+ * @param {String} className
525
+ * @returns {Boolean}
526
+ */
527
+ hasClass(className) {
528
+ if (className && className.indexOf('.') !== -1) {
529
+ console.log(
530
+ 'It looks like you\'re calling `ReactWrapper::hasClass()` with a CSS selector. ' +
531
+ 'hasClass() expects a class name, not a CSS selector.'
532
+ );
533
+ }
534
+ return this.single(n => instHasClassName(n, className));
535
+ }
536
+
537
+ /**
538
+ * Iterates through each node of the current wrapper and executes the provided function with a
539
+ * wrapper around the corresponding node passed in as the first argument.
540
+ *
541
+ * @param {Function} fn
542
+ * @returns {ReactWrapper}
543
+ */
544
+ forEach(fn) {
545
+ this.nodes.forEach((n, i) => fn.call(this, this.wrap(n), i));
546
+ return this;
547
+ }
548
+
549
+ /**
550
+ * Maps the current array of nodes to another array. Each node is passed in as a `ReactWrapper`
551
+ * to the map function.
552
+ *
553
+ * @param {Function} fn
554
+ * @returns {Array}
555
+ */
556
+ map(fn) {
557
+ return this.nodes.map((n, i) => fn.call(this, this.wrap(n), i));
558
+ }
559
+
560
+ /**
561
+ * Reduces the current array of nodes to another array.
562
+ * Each node is passed in as a `ShallowWrapper` to the reducer function.
563
+ *
564
+ * @param {Function} fn - the reducer function
565
+ * @param {*} initialValue - the initial value
566
+ * @returns {*}
567
+ */
568
+ reduce(fn, initialValue) {
569
+ return this.nodes.reduce(
570
+ (accum, n, i) => fn.call(this, accum, this.wrap(n), i),
571
+ initialValue
572
+ );
573
+ }
574
+
575
+ /**
576
+ * Reduces the current array of nodes to another array, from right to left. Each node is passed
577
+ * in as a `ShallowWrapper` to the reducer function.
578
+ *
579
+ * @param {Function} fn - the reducer function
580
+ * @param {*} initialValue - the initial value
581
+ * @returns {*}
582
+ */
583
+ reduceRight(fn, initialValue) {
584
+ return this.nodes.reduceRight(
585
+ (accum, n, i) => fn.call(this, accum, this.wrap(n), i),
586
+ initialValue
587
+ );
588
+ }
589
+
590
+ /**
591
+ * Returns whether or not any of the nodes in the wrapper match the provided selector.
592
+ *
593
+ * @param {Function|String} selector
594
+ * @returns {Boolean}
595
+ */
596
+ some(selector) {
597
+ const predicate = buildInstPredicate(selector);
598
+ return this.nodes.some(predicate);
599
+ }
600
+
601
+ /**
602
+ * Returns whether or not any of the nodes in the wrapper pass the provided predicate function.
603
+ *
604
+ * @param {Function} predicate
605
+ * @returns {Boolean}
606
+ */
607
+ someWhere(predicate) {
608
+ return this.nodes.some((n, i) => predicate.call(this, this.wrap(n), i));
609
+ }
610
+
611
+ /**
612
+ * Returns whether or not all of the nodes in the wrapper match the provided selector.
613
+ *
614
+ * @param {Function|String} selector
615
+ * @returns {Boolean}
616
+ */
617
+ every(selector) {
618
+ const predicate = buildInstPredicate(selector);
619
+ return this.nodes.every(predicate);
620
+ }
621
+
622
+ /**
623
+ * Returns whether or not any of the nodes in the wrapper pass the provided predicate function.
624
+ *
625
+ * @param {Function} predicate
626
+ * @returns {Boolean}
627
+ */
628
+ everyWhere(predicate) {
629
+ return this.nodes.every((n, i) => predicate.call(this, this.wrap(n), i));
630
+ }
631
+
632
+ /**
633
+ * Utility method used to create new wrappers with a mapping function that returns an array of
634
+ * nodes in response to a single node wrapper. The returned wrapper is a single wrapper around
635
+ * all of the mapped nodes flattened (and de-duplicated).
636
+ *
637
+ * @param {Function} fn
638
+ * @returns {ReactWrapper}
639
+ */
640
+ flatMap(fn) {
641
+ const nodes = this.nodes.map((n, i) => fn.call(this, this.wrap(n), i));
642
+ const flattened = flatten(nodes, true);
643
+ const uniques = unique(flattened);
644
+ return this.wrap(uniques);
645
+ }
646
+
647
+ /**
648
+ * Finds all nodes in the current wrapper nodes' render trees that match the provided predicate
649
+ * function.
650
+ *
651
+ * @param {Function} predicate
652
+ * @returns {ReactWrapper}
653
+ */
654
+ findWhere(predicate) {
655
+ return findWhereUnwrapped(this, n => predicate(this.wrap(n)));
656
+ }
657
+
658
+ /**
659
+ * Returns the node at a given index of the current wrapper.
660
+ *
661
+ * @param {Number} index
662
+ * @returns {ReactElement}
663
+ */
664
+ get(index) {
665
+ return this.nodes[index];
666
+ }
667
+
668
+ /**
669
+ * Returns a wrapper around the node at a given index of the current wrapper.
670
+ *
671
+ * @param {Number} index
672
+ * @returns {ReactWrapper}
673
+ */
674
+ at(index) {
675
+ return this.wrap(this.nodes[index]);
676
+ }
677
+
678
+ /**
679
+ * Returns a wrapper around the first node of the current wrapper.
680
+ *
681
+ * @returns {ReactWrapper}
682
+ */
683
+ first() {
684
+ return this.at(0);
685
+ }
686
+
687
+ /**
688
+ * Returns a wrapper around the last node of the current wrapper.
689
+ *
690
+ * @returns {ReactWrapper}
691
+ */
692
+ last() {
693
+ return this.at(this.length - 1);
694
+ }
695
+
696
+ /**
697
+ * Returns true if the current wrapper has no nodes. False otherwise.
698
+ *
699
+ * @returns {boolean}
700
+ */
701
+ isEmpty() {
702
+ return this.length === 0;
703
+ }
704
+
705
+ /**
706
+ * Utility method that throws an error if the current instance has a length other than one.
707
+ * This is primarily used to enforce that certain methods are only run on a wrapper when it is
708
+ * wrapping a single node.
709
+ *
710
+ * @param {Function} fn
711
+ * @returns {*}
712
+ */
713
+ single(fn) {
714
+ if (this.length !== 1) {
715
+ throw new Error(
716
+ `This method is only meant to be run on single node. ${this.length} found instead.`
717
+ );
718
+ }
719
+ return fn.call(this, this.node);
720
+ }
721
+
722
+ /**
723
+ * Helpful utility method to create a new wrapper with the same root as the current wrapper, with
724
+ * any nodes passed in as the first parameter automatically wrapped.
725
+ *
726
+ * @param {ReactWrapper|ReactElement|Array<ReactElement>} node
727
+ * @returns {ReactWrapper}
728
+ */
729
+ wrap(node) {
730
+ if (node instanceof ReactWrapper) {
731
+ return node;
732
+ }
733
+ return new ReactWrapper(node, this.root);
734
+ }
735
+
736
+ /**
737
+ * Returns an HTML-like string of the shallow render for debugging purposes.
738
+ *
739
+ * @returns {String}
740
+ */
741
+ debug() {
742
+ return debugInsts(this.nodes);
743
+ }
744
+
745
+ /**
746
+ * Invokes intercepter and returns itself. intercepter is called with itself.
747
+ * This is helpful when debugging nodes in method chains.
748
+ * @param fn
749
+ * @returns {ReactWrapper}
750
+ */
751
+ tap(intercepter) {
752
+ intercepter(this);
753
+ return this;
754
+ }
755
+
756
+ /**
757
+ * Detaches the react tree from the DOM. Runs `ReactDOM.unmountComponentAtNode()` under the hood.
758
+ *
759
+ * This method will most commonly be used as a "cleanup" method if you decide to use the
760
+ * `attachTo` option in `mount(node, options)`.
761
+ *
762
+ * The method is intentionally not "fluent" (in that it doesn't return `this`) because you should
763
+ * not be doing anything with this wrapper after this method is called.
764
+ */
765
+ detach() {
766
+ if (this.root !== this) {
767
+ throw new Error('ReactWrapper::detach() can only be called on the root');
768
+ }
769
+ if (!this.options.attachTo) {
770
+ throw new Error(
771
+ 'ReactWrapper::detach() can only be called on when the `attachTo` option was passed into ' +
772
+ '`mount()`.'
773
+ );
774
+ }
775
+ unmountComponentAtNode(this.options.attachTo);
776
+ }
777
+ }