UNPKG

20.6 kBJavaScriptView Raw
1import React from 'react';
2import { Popper } from 'react-popper';
3import user from '@testing-library/user-event';
4import { render, screen } from '@testing-library/react';
5import '@testing-library/jest-dom';
6import TooltipPopoverWrapper from '../TooltipPopoverWrapper';
7
8describe('Tooltip', () => {
9 let element;
10 let container;
11
12 beforeEach(() => {
13 element = document.createElement('div');
14 container = document.createElement('div');
15 element.innerHTML =
16 '<p id="target">This is the Tooltip <span id="innerTarget">target</span>.</p>';
17 element.setAttribute('id', 'testContainer');
18 container.setAttribute('id', 'container');
19 container.setAttribute('data-testid', 'container');
20 element.appendChild(container);
21 document.body.appendChild(element);
22
23 jest.useFakeTimers();
24 jest.resetModules();
25 Popper.mockClear();
26 });
27
28 afterEach(() => {
29 jest.clearAllTimers();
30 document.body.removeChild(element);
31 element = null;
32 container = null;
33 });
34
35 it('should render arrow by default', () => {
36 render(
37 <TooltipPopoverWrapper target="target" isOpen>
38 Tooltip Content
39 </TooltipPopoverWrapper>,
40 );
41
42 expect(document.querySelector('.arrow')).toBeInTheDocument();
43 });
44
45 it('should render not render arrow if hiderArrow is true', () => {
46 render(
47 <TooltipPopoverWrapper target="target" isOpen hideArrow>
48 Tooltip Content
49 </TooltipPopoverWrapper>,
50 );
51
52 expect(document.querySelector('.arrow')).not.toBeInTheDocument();
53 });
54
55 it('should not render children if isOpen is false', () => {
56 render(
57 <TooltipPopoverWrapper target="target" isOpen={false}>
58 Tooltip Content
59 </TooltipPopoverWrapper>,
60 );
61
62 expect(screen.queryByText(/tooltip content/i)).not.toBeInTheDocument();
63 });
64
65 it('should render if isOpen is true', () => {
66 render(
67 <TooltipPopoverWrapper
68 target="target"
69 isOpen
70 className="tooltip show"
71 trigger="hover"
72 >
73 Tooltip Content
74 </TooltipPopoverWrapper>,
75 );
76
77 expect(screen.queryByText(/tooltip content/i)).toBeInTheDocument();
78 expect(document.querySelector('.tooltip.show')).toBeInTheDocument();
79 });
80
81 it('should render with target object', () => {
82 render(
83 <TooltipPopoverWrapper
84 target={document.getElementById('target')}
85 isOpen
86 className="tooltip show"
87 >
88 Tooltip Content
89 </TooltipPopoverWrapper>,
90 );
91
92 expect(document.getElementsByClassName('tooltip show')).toHaveLength(1);
93 expect(screen.queryByText(/tooltip content/i)).toBeInTheDocument();
94 });
95
96 it('should toggle isOpen', () => {
97 const { rerender } = render(
98 <TooltipPopoverWrapper
99 target="target"
100 isOpen={false}
101 className="tooltip show"
102 >
103 Tooltip Content
104 </TooltipPopoverWrapper>,
105 );
106
107 expect(screen.queryByText(/tooltip content/i)).not.toBeInTheDocument();
108
109 rerender(
110 <TooltipPopoverWrapper target="target" isOpen className="tooltip show">
111 Tooltip Content
112 </TooltipPopoverWrapper>,
113 );
114
115 expect(screen.queryByText(/tooltip content/i)).toBeInTheDocument();
116
117 rerender(
118 <TooltipPopoverWrapper
119 target="target"
120 isOpen={false}
121 className="tooltip show"
122 >
123 Tooltip Content
124 </TooltipPopoverWrapper>,
125 );
126
127 jest.advanceTimersByTime(150);
128 expect(screen.queryByText(/tooltip content/i)).not.toBeInTheDocument();
129 });
130
131 it('should handle target clicks', () => {
132 const toggle = jest.fn();
133 const { rerender } = render(
134 <TooltipPopoverWrapper target="target" isOpen={false} toggle={toggle}>
135 Tooltip Content
136 </TooltipPopoverWrapper>,
137 );
138
139 user.click(screen.getByText(/this is the Tooltip/i));
140 jest.advanceTimersByTime(150);
141 expect(toggle).toBeCalled();
142 toggle.mockClear();
143
144 rerender(
145 <TooltipPopoverWrapper target="target" isOpen toggle={toggle}>
146 Tooltip Content
147 </TooltipPopoverWrapper>,
148 );
149
150 user.click(screen.getByText(/this is the Tooltip/i));
151 jest.advanceTimersByTime(150);
152 expect(toggle).toBeCalled();
153 });
154
155 it('should handle inner target clicks', () => {
156 const toggle = jest.fn();
157 render(
158 <TooltipPopoverWrapper target="target" isOpen={false} toggle={toggle}>
159 Tooltip Content
160 </TooltipPopoverWrapper>,
161 );
162
163 user.click(screen.getByText(/target/i));
164 jest.advanceTimersByTime(150);
165 expect(toggle).toBeCalled();
166 });
167
168 it('should not do anything when document click outside of target', () => {
169 const toggle = jest.fn();
170 render(
171 <TooltipPopoverWrapper target="target" isOpen={false} toggle={toggle}>
172 Tooltip Content
173 </TooltipPopoverWrapper>,
174 );
175
176 user.click(screen.getByTestId('container'));
177 expect(toggle).not.toBeCalled();
178 });
179
180 it('should open after receiving single touchstart and single click', () => {
181 const toggle = jest.fn();
182 render(
183 <TooltipPopoverWrapper
184 target="target"
185 isOpen={false}
186 toggle={toggle}
187 trigger="click"
188 >
189 Tooltip Content
190 </TooltipPopoverWrapper>,
191 );
192
193 user.click(screen.getByText(/target/i));
194 jest.advanceTimersByTime(200);
195
196 expect(toggle).toHaveBeenCalled();
197
198 // TODO: RTL currently doesn't support touch events
199 });
200
201 it('should close after receiving single touchstart and single click', () => {
202 const toggle = jest.fn();
203 render(
204 <TooltipPopoverWrapper
205 target="target"
206 isOpen
207 toggle={toggle}
208 trigger="click"
209 >
210 Tooltip Content
211 </TooltipPopoverWrapper>,
212 );
213
214 user.click(screen.getByText(/target/i));
215 jest.advanceTimersByTime(200);
216
217 expect(toggle).toHaveBeenCalled();
218
219 // TODO: RTL currently doesn't support touch events
220 });
221
222 it('should pass down custom modifiers', () => {
223 render(
224 <TooltipPopoverWrapper
225 isOpen
226 target="target"
227 modifiers={[
228 {
229 name: 'offset',
230 options: {
231 offset: [2, 2],
232 },
233 },
234 {
235 name: 'preventOverflow',
236 options: {
237 boundary: 'viewport',
238 },
239 },
240 ]}
241 >
242 Tooltip Content
243 </TooltipPopoverWrapper>,
244 );
245
246 expect(Popper.mock.calls[0][0].modifiers).toEqual(
247 expect.arrayContaining([
248 expect.objectContaining({
249 name: 'offset',
250 options: {
251 offset: [2, 2],
252 },
253 }),
254 ]),
255 );
256
257 expect(Popper.mock.calls[0][0].modifiers).toEqual(
258 expect.arrayContaining([
259 expect.objectContaining({
260 name: 'preventOverflow',
261 options: {
262 boundary: 'viewport',
263 },
264 }),
265 ]),
266 );
267 });
268
269 describe('PopperContent', () => {
270 beforeEach(() => {
271 jest.doMock('../PopperContent', () => {
272 return jest.fn((props) => {
273 return props.children({
274 update: () => {},
275 ref: () => {},
276 style: {},
277 placement: props.placement,
278 arrowProps: { ref: () => {}, style: {} },
279 isReferenceHidden: false,
280 });
281 });
282 });
283 });
284
285 it('should pass down cssModule', () => {
286 // eslint-disable-next-line global-require
287 const PopperContent = require('../PopperContent');
288 // eslint-disable-next-line global-require
289 const TooltipPopoverWrapper = require('../TooltipPopoverWrapper').default;
290
291 const cssModule = {
292 a: 'b',
293 };
294
295 render(
296 <TooltipPopoverWrapper isOpen target="target" cssModule={cssModule}>
297 Tooltip Content
298 </TooltipPopoverWrapper>,
299 );
300
301 expect(PopperContent).toBeCalledTimes(1);
302
303 expect(PopperContent.mock.calls[0][0]).toEqual(
304 expect.objectContaining({
305 cssModule: expect.objectContaining({
306 a: 'b',
307 }),
308 }),
309 );
310 });
311
312 it('should pass down offset', () => {
313 // eslint-disable-next-line global-require
314 const PopperContent = require('../PopperContent');
315 // eslint-disable-next-line global-require
316 const TooltipPopoverWrapper = require('../TooltipPopoverWrapper').default;
317
318 render(
319 <TooltipPopoverWrapper isOpen target="target" offset={[0, 12]}>
320 Tooltip content
321 </TooltipPopoverWrapper>,
322 );
323
324 expect(PopperContent).toBeCalledTimes(1);
325 expect(PopperContent.mock.calls[0][0].offset).toEqual(
326 expect.arrayContaining([0, 12]),
327 );
328 });
329
330 it('should pass down flip', () => {
331 // eslint-disable-next-line global-require
332 const PopperContent = require('../PopperContent');
333 // eslint-disable-next-line global-require
334 const TooltipPopoverWrapper = require('../TooltipPopoverWrapper').default;
335
336 render(
337 <TooltipPopoverWrapper isOpen target="target" flip={false}>
338 Tooltip Content
339 </TooltipPopoverWrapper>,
340 );
341
342 expect(PopperContent).toBeCalledTimes(1);
343 expect(PopperContent.mock.calls[0][0].flip).toBe(false);
344 });
345
346 it('should handle inner target click and correct placement', () => {
347 const toggle = jest.fn();
348 // eslint-disable-next-line global-require
349 const PopperContent = require('../PopperContent');
350 // eslint-disable-next-line global-require
351 const TooltipPopoverWrapper = require('../TooltipPopoverWrapper').default;
352
353 const { rerender } = render(
354 <TooltipPopoverWrapper target="target" isOpen={false} toggle={toggle}>
355 Tooltip Content
356 </TooltipPopoverWrapper>,
357 );
358
359 user.click(screen.getByText(/target/i));
360 jest.advanceTimersByTime(200);
361 expect(toggle).toBeCalled();
362
363 rerender(
364 <TooltipPopoverWrapper target="target" isOpen toggle={toggle}>
365 Tooltip Content
366 </TooltipPopoverWrapper>,
367 );
368
369 expect(PopperContent.mock.calls[0][0].target.id).toBe('target');
370 });
371 });
372
373 it('should not call props.toggle when disabled ', () => {
374 const toggle = jest.fn();
375
376 render(
377 <TooltipPopoverWrapper target="target" disabled isOpen toggle={toggle}>
378 Tooltip Content
379 </TooltipPopoverWrapper>,
380 );
381
382 user.click(screen.getByText(/target/i));
383
384 expect(toggle).not.toHaveBeenCalled();
385 });
386
387 it('should not throw when props.toggle is not provided ', () => {
388 render(
389 <TooltipPopoverWrapper target="target" disabled isOpen>
390 Tooltip Content
391 </TooltipPopoverWrapper>,
392 );
393
394 user.click(screen.getByText(/target/i));
395 });
396
397 it('should not throw when passed a ref object as the target', () => {
398 const targetObj = React.createRef();
399
400 targetObj.current = {
401 addEventListener: jest.fn(),
402 removeEventListener: jest.fn(),
403 };
404
405 const { unmount } = render(
406 <TooltipPopoverWrapper isOpen={false} target={targetObj}>
407 Yo!
408 </TooltipPopoverWrapper>,
409 );
410
411 unmount();
412
413 expect(targetObj.current.addEventListener).toHaveBeenCalled();
414 expect(targetObj.current.removeEventListener).toHaveBeenCalled();
415 });
416
417 describe('multi target', () => {
418 let targets;
419 let targetContainer;
420 beforeEach(() => {
421 targetContainer = document.createElement('div');
422 targetContainer.innerHTML =
423 "<span class='example first'>Target 1</span><span class='example second'>Target 2<span class='inner_example'>Inner target</span></span>";
424 element.appendChild(targetContainer);
425 targets = targetContainer.querySelectorAll('.example');
426 });
427
428 afterEach(() => {
429 element.removeChild(targetContainer);
430 targets = null;
431 });
432
433 it('should attach tooltip on multiple target when a target selector matches multiple elements', () => {
434 const toggle = jest.fn();
435 render(
436 <TooltipPopoverWrapper
437 target=".example"
438 isOpen={false}
439 toggle={toggle}
440 delay={0}
441 >
442 Yo!
443 </TooltipPopoverWrapper>,
444 );
445
446 user.click(targets[0]);
447 jest.advanceTimersByTime(200);
448 expect(toggle).toHaveBeenCalledTimes(1);
449
450 user.click(targets[1]);
451 jest.advanceTimersByTime(200);
452 expect(toggle).toHaveBeenCalledTimes(2);
453 });
454
455 it('should attach tooltip on second target with correct placement, when inner element is clicked', () => {
456 const toggle = jest.fn();
457 render(
458 <TooltipPopoverWrapper
459 target=".example"
460 isOpen={false}
461 toggle={toggle}
462 delay={0}
463 >
464 Yo!
465 </TooltipPopoverWrapper>,
466 );
467
468 user.click(targets[0]);
469 jest.advanceTimersByTime(200);
470 expect(toggle).toHaveBeenCalledTimes(1);
471 });
472 });
473
474 describe('delay', () => {
475 it('should accept a number', () => {
476 const toggle = jest.fn();
477 render(
478 <TooltipPopoverWrapper
479 target="target"
480 isOpen
481 toggle={toggle}
482 delay={200}
483 >
484 Tooltip Content
485 </TooltipPopoverWrapper>,
486 );
487
488 user.click(screen.getByText(/target/i));
489
490 jest.advanceTimersByTime(100);
491 expect(toggle).not.toBeCalled();
492 jest.advanceTimersByTime(100);
493 expect(toggle).toBeCalled();
494 });
495
496 it('should accept an object', () => {
497 const toggle = jest.fn();
498 render(
499 <TooltipPopoverWrapper
500 target="target"
501 isOpen
502 toggle={toggle}
503 delay={{ show: 400, hide: 400 }}
504 >
505 Tooltip Content
506 </TooltipPopoverWrapper>,
507 );
508
509 user.click(screen.getByText(/target/i));
510
511 jest.advanceTimersByTime(200);
512 expect(toggle).not.toBeCalled();
513 jest.advanceTimersByTime(200);
514 expect(toggle).toBeCalled();
515 });
516
517 it('should use default value if value is missing from object', () => {
518 const toggle = jest.fn();
519 render(
520 <TooltipPopoverWrapper
521 target="target"
522 isOpen
523 toggle={toggle}
524 delay={{ show: 0 }}
525 >
526 Tooltip Content
527 </TooltipPopoverWrapper>,
528 );
529
530 user.click(screen.getByText(/target/i));
531
532 jest.advanceTimersByTime(10);
533 expect(toggle).not.toBeCalled();
534 jest.advanceTimersByTime(40); // default hide value is 50
535 expect(toggle).toBeCalled();
536 });
537 });
538
539 describe('hide', () => {
540 it('should call toggle when isOpen', () => {
541 const toggle = jest.fn();
542 render(
543 <TooltipPopoverWrapper target="target" isOpen toggle={toggle}>
544 Tooltip Content
545 </TooltipPopoverWrapper>,
546 );
547
548 user.click(screen.getByText(/target/i));
549
550 jest.advanceTimersByTime(200);
551 expect(toggle).toHaveBeenCalled();
552 });
553 });
554
555 describe('show', () => {
556 it('should call toggle when isOpen', () => {
557 const toggle = jest.fn();
558 render(
559 <TooltipPopoverWrapper target="target" isOpen={false} toggle={toggle}>
560 Tooltip Content
561 </TooltipPopoverWrapper>,
562 );
563
564 user.click(screen.getByText(/target/i));
565
566 jest.advanceTimersByTime(200);
567 expect(toggle).toHaveBeenCalled();
568 });
569 });
570
571 describe('onMouseOverTooltip', () => {
572 it('should clear timeout if it exists on target click', () => {
573 const toggle = jest.fn();
574 const { rerender } = render(
575 <TooltipPopoverWrapper
576 target="target"
577 isOpen={false}
578 toggle={toggle}
579 delay={200}
580 trigger="hover"
581 >
582 Tooltip Content
583 </TooltipPopoverWrapper>,
584 );
585
586 user.hover(screen.getByText(/target/i));
587
588 rerender(
589 <TooltipPopoverWrapper
590 target="target"
591 isOpen
592 toggle={toggle}
593 delay={200}
594 trigger="hover"
595 >
596 Tooltip Content
597 </TooltipPopoverWrapper>,
598 );
599
600 user.unhover(screen.getByText(/target/i));
601
602 jest.advanceTimersByTime(200);
603 expect(toggle).toHaveBeenCalledTimes(1);
604 });
605
606 it('should not call .toggle if isOpen', () => {
607 const toggle = jest.fn();
608 render(
609 <TooltipPopoverWrapper
610 target="target"
611 isOpen
612 toggle={toggle}
613 delay={200}
614 trigger="hover"
615 >
616 Tooltip Content
617 </TooltipPopoverWrapper>,
618 );
619
620 user.hover(screen.getByText(/target/i));
621 jest.advanceTimersByTime(200);
622 expect(toggle).not.toHaveBeenCalled();
623 });
624 });
625
626 describe('onMouseLeaveTooltip', () => {
627 it('should clear timeout if it exists on target click', () => {
628 const toggle = jest.fn();
629 const { rerender } = render(
630 <TooltipPopoverWrapper
631 target="target"
632 isOpen
633 toggle={toggle}
634 delay={200}
635 trigger="hover"
636 >
637 Tooltip Content
638 </TooltipPopoverWrapper>,
639 );
640
641 user.unhover(screen.getByText(/target/i));
642
643 rerender(
644 <TooltipPopoverWrapper
645 target="target"
646 isOpen={false}
647 toggle={toggle}
648 delay={200}
649 trigger="hover"
650 >
651 Tooltip Content
652 </TooltipPopoverWrapper>,
653 );
654
655 user.hover(screen.getByText(/target/i));
656
657 jest.advanceTimersByTime(200);
658 expect(toggle).toHaveBeenCalledTimes(1);
659 });
660
661 it('should not call .toggle if isOpen is false', () => {
662 const toggle = jest.fn();
663 render(
664 <TooltipPopoverWrapper
665 target="target"
666 isOpen={false}
667 toggle={toggle}
668 delay={200}
669 trigger="hover"
670 >
671 Tooltip Content
672 </TooltipPopoverWrapper>,
673 );
674
675 user.unhover(screen.getByText(/target/i));
676 jest.advanceTimersByTime(200);
677 expect(toggle).not.toHaveBeenCalled();
678 });
679 });
680
681 describe('autohide', () => {
682 it('should keep Tooltip around when false and onmouseleave from Tooltip content', () => {
683 const toggle = jest.fn();
684 render(
685 <TooltipPopoverWrapper
686 trigger="hover"
687 target="target"
688 autohide={false}
689 isOpen
690 toggle={toggle}
691 delay={200}
692 >
693 Tooltip Content
694 </TooltipPopoverWrapper>,
695 );
696
697 user.hover(screen.getByText(/tooltip content/i));
698 jest.advanceTimersByTime(200);
699 expect(toggle).not.toHaveBeenCalled();
700 user.unhover(screen.getByText(/tooltip content/i));
701 jest.advanceTimersByTime(200);
702 expect(toggle).toHaveBeenCalled();
703 });
704
705 it('clears showTimeout and hideTimeout in onMouseLeaveTooltipContent', () => {
706 const toggle = jest.fn();
707 render(
708 <TooltipPopoverWrapper
709 trigger="hover"
710 target="target"
711 autohide={false}
712 isOpen
713 toggle={toggle}
714 delay={200}
715 >
716 Tooltip Content
717 </TooltipPopoverWrapper>,
718 );
719
720 user.unhover(screen.getByText(/tooltip content/i));
721 user.hover(screen.getByText(/tooltip content/i));
722 user.unhover(screen.getByText(/tooltip content/i));
723
724 jest.advanceTimersByTime(200);
725
726 expect(toggle).toBeCalledTimes(1);
727 });
728
729 it('should not keep Tooltip around when autohide is true and Tooltip content is hovered over', () => {
730 const toggle = jest.fn();
731 render(
732 <TooltipPopoverWrapper
733 target="target"
734 autohide
735 isOpen
736 toggle={toggle}
737 delay={200}
738 trigger="click hover focus"
739 >
740 Tooltip Content
741 </TooltipPopoverWrapper>,
742 );
743
744 user.unhover(screen.getByText(/target/i));
745 user.hover(screen.getByText(/tooltip content/i));
746
747 jest.advanceTimersByTime(200);
748
749 expect(toggle).toHaveBeenCalled();
750 });
751
752 it('should allow a function to be used as children', () => {
753 const renderChildren = jest.fn();
754 render(
755 <TooltipPopoverWrapper target="target" isOpen>
756 {renderChildren}
757 </TooltipPopoverWrapper>,
758 );
759 expect(renderChildren).toHaveBeenCalled();
760 });
761
762 it('should render children properly when children is a function', () => {
763 render(
764 <TooltipPopoverWrapper
765 target="target"
766 isOpen
767 className="tooltip show"
768 trigger="hover"
769 >
770 {() => 'Tooltip Content'}
771 </TooltipPopoverWrapper>,
772 );
773
774 expect(screen.getByText(/tooltip content/i)).toBeInTheDocument();
775 });
776 });
777});