UNPKG

22.1 kBJavaScriptView Raw
1import React from 'react';
2import { render, screen } from '@testing-library/react';
3import '@testing-library/jest-dom';
4import user from '@testing-library/user-event';
5import { Carousel } from '..';
6import CarouselItem from '../CarouselItem';
7import CarouselIndicators from '../CarouselIndicators';
8import CarouselControl from '../CarouselControl';
9import CarouselCaption from '../CarouselCaption';
10import { CarouselContext } from '../CarouselContext';
11
12const DEFAULT_TIMER_TIME = 600;
13
14describe('Carousel', () => {
15 beforeEach(() => {
16 jest.useFakeTimers();
17 });
18
19 afterEach(() => {
20 jest.clearAllTimers();
21 });
22
23 const items = [
24 { src: '', altText: 'a', caption: 'caption 1' },
25 { src: '', altText: 'b', caption: 'caption 2' },
26 { src: '', altText: 'c', caption: 'caption 3' },
27 ];
28
29 const slides = items.map((item, idx) => {
30 return (
31 <CarouselItem key={idx}>
32 <CarouselCaption
33 captionText={item.caption}
34 captionHeader={item.caption}
35 />
36 </CarouselItem>
37 );
38 });
39
40 describe('captions', () => {
41 it('should render a header and a caption', () => {
42 render(<CarouselCaption captionHeader="abc" captionText="def" />);
43 expect(screen.getByText('abc').tagName.toLowerCase()).toBe('h3');
44 expect(screen.getByText('def').tagName.toLowerCase()).toBe('p');
45 });
46 });
47
48 describe('items', () => {
49 it('should render custom tag', () => {
50 render(<CarouselItem tag="main">Hello</CarouselItem>);
51 expect(screen.getByText(/hello/i).tagName.toLowerCase()).toBe('main');
52 });
53
54 it('should render an image if one is passed in', () => {
55 render(
56 <CarouselItem>
57 <img src={items[0].src} alt={items[0].altText} />
58 </CarouselItem>,
59 );
60 expect(screen.getByAltText(items[0].altText)).toBeInTheDocument();
61 });
62
63 it('should render a caption if one is passed in', () => {
64 render(
65 <CarouselItem>
66 <CarouselCaption captionHeader="header" captionText="text" />
67 </CarouselItem>,
68 );
69 expect(screen.getByText('header')).toBeInTheDocument();
70 expect(screen.getByText('text')).toBeInTheDocument();
71 });
72
73 describe('transitions', () => {
74 it('should add the appropriate classes when entering right', () => {
75 const wrapper = ({ children }) => (
76 <CarouselContext.Provider value={{ direction: 'end' }}>
77 {children}
78 </CarouselContext.Provider>
79 );
80
81 const { rerender } = render(
82 <CarouselItem in={false}>the mandalorian</CarouselItem>,
83 { wrapper },
84 );
85 rerender(<CarouselItem in>the mandalorian</CarouselItem>);
86 expect(screen.getByText(/the mandalorian/i)).toHaveClass(
87 'carousel-item carousel-item-start carousel-item-next',
88 );
89 jest.advanceTimersByTime(DEFAULT_TIMER_TIME);
90 expect(screen.getByText(/the mandalorian/i)).toHaveClass(
91 'carousel-item active',
92 );
93 rerender(<CarouselItem in={false}>the mandalorian</CarouselItem>);
94 expect(screen.getByText(/the mandalorian/i)).toHaveClass(
95 'carousel-item active carousel-item-start',
96 );
97 jest.advanceTimersByTime(DEFAULT_TIMER_TIME);
98 expect(screen.getByText(/the mandalorian/i)).toHaveClass(
99 'carousel-item',
100 );
101 });
102
103 it('should add the appropriate classes when entering left', () => {
104 const wrapper = ({ children }) => (
105 <CarouselContext.Provider value={{ direction: 'start' }}>
106 {children}
107 </CarouselContext.Provider>
108 );
109
110 const { rerender } = render(
111 <CarouselItem in={false}>the mandalorian</CarouselItem>,
112 { wrapper },
113 );
114 rerender(<CarouselItem in>the mandalorian</CarouselItem>);
115 expect(screen.getByText(/the mandalorian/i)).toHaveClass(
116 'carousel-item carousel-item-end carousel-item-prev',
117 );
118 jest.advanceTimersByTime(DEFAULT_TIMER_TIME);
119 expect(screen.getByText(/the mandalorian/i)).toHaveClass(
120 'carousel-item active',
121 );
122 rerender(<CarouselItem in={false}>the mandalorian</CarouselItem>);
123 expect(screen.getByText(/the mandalorian/i)).toHaveClass(
124 'carousel-item active carousel-item-end',
125 );
126 jest.advanceTimersByTime(DEFAULT_TIMER_TIME);
127 expect(screen.getByText(/the mandalorian/i)).toHaveClass(
128 'carousel-item',
129 );
130 });
131
132 it('should call all callbacks when transitioning in and out', () => {
133 const callbacks = {
134 onEnter: jest.fn(),
135 onEntering: jest.fn(),
136 onEntered: jest.fn(),
137 onExit: jest.fn(),
138 onExiting: jest.fn(),
139 onExited: jest.fn(),
140 };
141 const { rerender } = render(<CarouselItem in={false} {...callbacks} />);
142 rerender(<CarouselItem in {...callbacks} />);
143 expect(callbacks.onEnter).toHaveBeenCalled();
144 expect(callbacks.onEntering).toHaveBeenCalled();
145 expect(callbacks.onEntered).not.toHaveBeenCalled();
146 jest.advanceTimersByTime(DEFAULT_TIMER_TIME);
147 expect(callbacks.onEntered).toHaveBeenCalled();
148 expect(callbacks.onExit).not.toHaveBeenCalled();
149
150 rerender(<CarouselItem in={false} {...callbacks} />);
151 expect(callbacks.onExit).toHaveBeenCalled();
152 expect(callbacks.onExiting).toHaveBeenCalled();
153 expect(callbacks.onExited).not.toHaveBeenCalled();
154 jest.advanceTimersByTime(DEFAULT_TIMER_TIME);
155 expect(callbacks.onExiting).toHaveBeenCalled();
156 expect(callbacks.onExited).toHaveBeenCalled();
157 });
158 });
159 });
160
161 describe('indicators', () => {
162 it('should render a list with the right number of items', () => {
163 render(
164 <CarouselIndicators
165 items={items}
166 activeIndex={0}
167 onClickHandler={() => {}}
168 />,
169 );
170 expect(screen.getAllByLabelText(/caption/i).length).toBe(3);
171 });
172
173 it('should append the correct active class', () => {
174 render(
175 <CarouselIndicators
176 items={items}
177 activeIndex={0}
178 onClickHandler={() => {}}
179 />,
180 );
181 expect(screen.getByLabelText(/caption 1/i)).toHaveClass('active');
182 });
183
184 it('should call the click hanlder', () => {
185 const onClick = jest.fn();
186 render(
187 <CarouselIndicators
188 items={items}
189 activeIndex={0}
190 onClickHandler={onClick}
191 />,
192 );
193 user.click(screen.getByLabelText(/caption 1/i));
194 expect(onClick).toHaveBeenCalled();
195 });
196 });
197
198 describe('controls', () => {
199 it('should render an anchor tag', () => {
200 render(<CarouselControl direction="next" onClickHandler={() => {}} />);
201 expect(screen.getByRole('button').tagName.toLowerCase()).toBe('a');
202 });
203
204 it('should call the onClickHandler', () => {
205 const onClick = jest.fn();
206 render(<CarouselControl direction="next" onClickHandler={onClick} />);
207 user.click(screen.getByRole('button'));
208 expect(onClick).toHaveBeenCalled();
209 });
210 });
211
212 describe('rendering', () => {
213 it('should show the carousel indicators', () => {
214 render(
215 <Carousel activeIndex={0} next={() => {}} previous={() => {}}>
216 <CarouselIndicators
217 items={items}
218 data-testid="c3po"
219 activeIndex={0}
220 onClickHandler={() => {}}
221 />
222 {slides}
223 </Carousel>,
224 );
225 expect(screen.getByTestId('c3po')).toHaveClass('carousel-indicators');
226 });
227
228 it('should show controls', () => {
229 render(
230 <Carousel activeIndex={0} next={() => {}} previous={() => {}}>
231 {slides}
232 <CarouselControl
233 direction="prev"
234 directionText="Previous"
235 onClickHandler={() => {}}
236 />
237 <CarouselControl
238 direction="next"
239 directionText="Next"
240 onClickHandler={() => {}}
241 />
242 </Carousel>,
243 );
244 screen.getAllByRole('button').forEach((element) => {
245 expect(element.className).toMatch(/carousel-control/i);
246 });
247 });
248
249 it('should show a single slide', () => {
250 render(
251 <Carousel
252 activeIndex={0}
253 next={() => {}}
254 previous={() => {}}
255 data-testid="carousel"
256 >
257 {slides}
258 </Carousel>,
259 );
260 expect(
261 screen.getByTestId('carousel').getElementsByClassName('active').length,
262 ).toBe(1);
263 });
264
265 it('should show indicators and controls', () => {
266 render(
267 <Carousel activeIndex={0} next={() => {}} previous={() => {}}>
268 <CarouselIndicators
269 items={items}
270 data-testid="carousel-indicator"
271 activeIndex={0}
272 onClickHandler={() => {}}
273 />
274 {slides}
275 <CarouselControl
276 direction="prev"
277 data-testid="prev"
278 directionText="Previous"
279 onClickHandler={() => {}}
280 />
281 <CarouselControl
282 direction="next"
283 data-testid="next"
284 directionText="Next"
285 onClickHandler={() => {}}
286 />
287 </Carousel>,
288 );
289
290 expect(screen.getByTestId('carousel-indicator')).toBeInTheDocument();
291 expect(screen.getByTestId('prev')).toBeInTheDocument();
292 expect(screen.getByTestId('next')).toBeInTheDocument();
293 });
294
295 it('should tolerate booleans, null and undefined values rendered as children of Carousel', () => {
296 render(
297 <Carousel activeIndex={0} next={() => {}} previous={() => {}}>
298 {null}
299 {true}
300 {false}
301 {undefined}
302 {(() => {})()}
303 <CarouselIndicators
304 items={items}
305 data-testid="carousel-indicator"
306 activeIndex={0}
307 onClickHandler={() => {}}
308 />
309 {slides}
310 <CarouselControl
311 direction="prev"
312 data-testid="prev"
313 directionText="Previous"
314 onClickHandler={() => {}}
315 />
316 <CarouselControl
317 direction="next"
318 data-testid="next"
319 directionText="Next"
320 onClickHandler={() => {}}
321 />
322 </Carousel>,
323 );
324
325 expect(screen.getByTestId('carousel-indicator')).toBeInTheDocument();
326 expect(screen.getByTestId('prev')).toBeInTheDocument();
327 expect(screen.getByTestId('next')).toBeInTheDocument();
328 });
329
330 it('should not have the class "carousel-dark" by default', () => {
331 render(
332 <Carousel
333 data-testid="star-wars"
334 activeIndex={0}
335 next={() => {}}
336 previous={() => {}}
337 >
338 {slides}
339 </Carousel>,
340 );
341
342 expect(screen.getByTestId('star-wars')).not.toHaveClass('carousel-dark');
343 });
344
345 it('should have the class "carousel-dark" when dark prop is true', () => {
346 render(
347 <Carousel
348 data-testid="star-wars"
349 dark
350 activeIndex={0}
351 next={() => {}}
352 previous={() => {}}
353 >
354 {slides}
355 </Carousel>,
356 );
357
358 expect(screen.getByTestId('star-wars')).toHaveClass('carousel-dark');
359 });
360 });
361
362 describe('carouseling', () => {
363 const carouselItems = [
364 { src: '', altText: 'a', caption: 'Grogu' },
365 { src: '', altText: 'b', caption: 'Boba Fett' },
366 { src: '', altText: 'c', caption: 'The Mandalorian' },
367 ];
368
369 const carouselSlides = carouselItems.map((item, idx) => {
370 return <CarouselItem key={idx}>{item.caption}</CarouselItem>;
371 });
372
373 it('should set second slide to active if second indicator clicked', () => {
374 const { rerender } = render(
375 <Carousel activeIndex={0} next={() => {}} previous={() => {}}>
376 <CarouselIndicators
377 items={carouselItems}
378 data-testid="boba-fett"
379 activeIndex={0}
380 onClickHandler={() => function () {}}
381 />
382 {carouselSlides}
383 <CarouselControl
384 direction="prev"
385 directionText="Previous"
386 onClickHandler={() => {}}
387 />
388 <CarouselControl
389 direction="next"
390 directionText="Next"
391 onClickHandler={() => {}}
392 />
393 </Carousel>,
394 );
395
396 user.click(screen.getByLabelText(/boba fett/i));
397
398 rerender(
399 <Carousel activeIndex={1} next={() => {}} previous={() => {}}>
400 <CarouselIndicators
401 items={carouselItems}
402 data-testid="boba-fett"
403 activeIndex={1}
404 onClickHandler={() => function () {}}
405 />
406 {carouselSlides}
407 <CarouselControl
408 direction="prev"
409 directionText="Previous"
410 onClickHandler={() => {}}
411 />
412 <CarouselControl
413 direction="next"
414 directionText="Next"
415 onClickHandler={() => {}}
416 />
417 </Carousel>,
418 );
419
420 expect(screen.getByText(/boba fett/i)).toHaveClass(
421 'carousel-item carousel-item-start carousel-item-next',
422 );
423 jest.advanceTimersByTime(DEFAULT_TIMER_TIME);
424 expect(screen.getByText(/boba fett/i)).toHaveClass(
425 'carousel-item active',
426 );
427 });
428
429 it('should go right when the index increases', () => {
430 const { rerender } = render(
431 <Carousel
432 interval={1000}
433 activeIndex={0}
434 next={() => {}}
435 previous={() => {}}
436 >
437 {carouselSlides}
438 </Carousel>,
439 );
440
441 rerender(
442 <Carousel
443 interval={1000}
444 activeIndex={1}
445 next={() => {}}
446 previous={() => {}}
447 >
448 {carouselSlides}
449 </Carousel>,
450 );
451 expect(screen.getByText(/boba fett/i)).toHaveClass(
452 'carousel-item carousel-item-start carousel-item-next',
453 );
454 jest.advanceTimersByTime(DEFAULT_TIMER_TIME);
455 expect(screen.getByText(/boba fett/i)).toHaveClass('active');
456 });
457
458 it('should go left when the index decreases', () => {
459 const { rerender } = render(
460 <Carousel
461 interval={1000}
462 activeIndex={1}
463 next={() => {}}
464 previous={() => {}}
465 >
466 {carouselSlides}
467 </Carousel>,
468 );
469
470 rerender(
471 <Carousel
472 interval={1000}
473 activeIndex={0}
474 next={() => {}}
475 previous={() => {}}
476 >
477 {carouselSlides}
478 </Carousel>,
479 );
480 expect(screen.getByText(/grogu/i)).toHaveClass(
481 'carousel-item carousel-item-prev carousel-item-end',
482 );
483 jest.advanceTimersByTime(DEFAULT_TIMER_TIME);
484 expect(screen.getByText(/grogu/i)).toHaveClass('active');
485 });
486
487 it('should go right if transitioning from the last to first slide by non-indicator', () => {
488 const { rerender } = render(
489 <Carousel
490 interval={1000}
491 activeIndex={2}
492 next={() => {}}
493 previous={() => {}}
494 >
495 {carouselSlides}
496 </Carousel>,
497 );
498
499 rerender(
500 <Carousel
501 interval={1000}
502 activeIndex={0}
503 next={() => {}}
504 previous={() => {}}
505 >
506 {carouselSlides}
507 </Carousel>,
508 );
509 expect(screen.getByText(/grogu/i)).toHaveClass(
510 'carousel-item carousel-item-start carousel-item-next',
511 );
512 jest.advanceTimersByTime(DEFAULT_TIMER_TIME);
513 expect(screen.getByText(/grogu/i)).toHaveClass('active');
514 });
515
516 it('should go left if transitioning from the last to first slide by indicator', () => {
517 const { rerender } = render(
518 <Carousel
519 interval={1000}
520 activeIndex={2}
521 next={() => {}}
522 previous={() => {}}
523 >
524 <CarouselIndicators
525 items={carouselItems}
526 activeIndex={2}
527 onClickHandler={() => {}}
528 />
529 {carouselSlides}
530 <CarouselControl
531 direction="prev"
532 directionText="Previous"
533 onClickHandler={() => {}}
534 />
535 <CarouselControl
536 direction="next"
537 directionText="Next"
538 onClickHandler={() => {}}
539 />
540 </Carousel>,
541 );
542
543 user.click(screen.getByLabelText(/grogu/i));
544
545 rerender(
546 <Carousel
547 interval={1000}
548 activeIndex={0}
549 next={() => {}}
550 previous={() => {}}
551 >
552 <CarouselIndicators
553 items={carouselItems}
554 activeIndex={0}
555 onClickHandler={() => {}}
556 />
557 {carouselSlides}
558 <CarouselControl
559 direction="prev"
560 directionText="Previous"
561 onClickHandler={() => {}}
562 />
563 <CarouselControl
564 direction="next"
565 directionText="Next"
566 onClickHandler={() => {}}
567 />
568 </Carousel>,
569 );
570
571 expect(screen.getByText(/grogu/i)).toHaveClass(
572 'carousel-item carousel-item-end carousel-item-prev',
573 );
574 });
575
576 it('should go left if transitioning from the first to last slide by non-indicator', () => {
577 const { rerender } = render(
578 <Carousel
579 interval={1000}
580 activeIndex={0}
581 next={() => {}}
582 previous={() => {}}
583 >
584 {carouselSlides}
585 </Carousel>,
586 );
587
588 rerender(
589 <Carousel
590 interval={1000}
591 activeIndex={2}
592 next={() => {}}
593 previous={() => {}}
594 >
595 {carouselSlides}
596 </Carousel>,
597 );
598
599 expect(screen.getByText(/the mandalorian/i)).toHaveClass(
600 'carousel-item carousel-item-end carousel-item-prev',
601 );
602 jest.advanceTimersByTime(DEFAULT_TIMER_TIME);
603 expect(screen.getByText(/the mandalorian/i)).toHaveClass('active');
604 });
605
606 it('should go right if transitioning from the first to last slide by indicator', () => {
607 const { rerender } = render(
608 <Carousel
609 interval={1000}
610 activeIndex={0}
611 next={() => {}}
612 previous={() => {}}
613 >
614 <CarouselIndicators
615 items={carouselItems}
616 activeIndex={0}
617 onClickHandler={() => {}}
618 />
619 {carouselSlides}
620 <CarouselControl
621 direction="prev"
622 directionText="Previous"
623 onClickHandler={() => {}}
624 />
625 <CarouselControl
626 direction="next"
627 directionText="Next"
628 onClickHandler={() => {}}
629 />
630 </Carousel>,
631 );
632
633 user.click(screen.getByLabelText(/the mandalorian/i));
634
635 rerender(
636 <Carousel
637 interval={1000}
638 activeIndex={2}
639 next={() => {}}
640 previous={() => {}}
641 >
642 <CarouselIndicators
643 items={carouselItems}
644 activeIndex={2}
645 onClickHandler={() => {}}
646 />
647 {carouselSlides}
648 <CarouselControl
649 direction="prev"
650 directionText="Previous"
651 onClickHandler={() => {}}
652 />
653 <CarouselControl
654 direction="next"
655 directionText="Next"
656 onClickHandler={() => {}}
657 />
658 </Carousel>,
659 );
660
661 expect(screen.getByText(/the mandalorian/i)).toHaveClass(
662 'carousel-item carousel-item-start carousel-item-next',
663 );
664 jest.advanceTimersByTime(DEFAULT_TIMER_TIME);
665 expect(screen.getByText(/the mandalorian/i)).toHaveClass('active');
666 });
667 });
668
669 describe('interval', () => {
670 it('should not autoplay by default', () => {
671 const next = jest.fn();
672 render(
673 <Carousel
674 next={next}
675 previous={() => {}}
676 interval={1000}
677 activeIndex={0}
678 >
679 {slides}
680 </Carousel>,
681 );
682 jest.advanceTimersByTime(1000);
683 expect(next).not.toHaveBeenCalled();
684 });
685
686 it('should autoplay when ride is carousel', () => {
687 const next = jest.fn();
688 render(
689 <Carousel
690 next={next}
691 previous={() => {}}
692 interval={1000}
693 activeIndex={0}
694 ride="carousel"
695 >
696 {slides}
697 </Carousel>,
698 );
699 jest.advanceTimersByTime(1000);
700 expect(next).toHaveBeenCalled();
701 });
702
703 it('should accept a number', () => {
704 const next = jest.fn();
705 render(
706 <Carousel
707 next={next}
708 previous={() => {}}
709 interval={1000}
710 activeIndex={0}
711 ride="carousel"
712 >
713 {slides}
714 </Carousel>,
715 );
716 jest.advanceTimersByTime(1000);
717 expect(next).toHaveBeenCalled();
718 });
719
720 it('should accept a boolean', () => {
721 const next = jest.fn();
722 render(
723 <Carousel
724 next={next}
725 previous={() => {}}
726 activeIndex={0}
727 interval={false}
728 >
729 {slides}
730 </Carousel>,
731 );
732 jest.advanceTimersByTime(5000);
733 expect(next).not.toHaveBeenCalled();
734 });
735
736 it('should default to 5000', () => {
737 const next = jest.fn();
738 render(
739 <Carousel
740 next={next}
741 previous={() => {}}
742 activeIndex={0}
743 ride="carousel"
744 >
745 {slides}
746 </Carousel>,
747 );
748 jest.advanceTimersByTime(5000);
749 expect(next).toHaveBeenCalled();
750 });
751
752 it('it should accept a string', () => {
753 const next = jest.fn();
754 render(
755 <Carousel
756 next={next}
757 previous={() => {}}
758 interval="1000"
759 activeIndex={0}
760 ride="carousel"
761 >
762 {slides}
763 </Carousel>,
764 );
765 jest.advanceTimersByTime(1000);
766 expect(next).toHaveBeenCalled();
767 });
768 });
769});