diff --git a/test/ToastBodySpec.js b/test/ToastBodySpec.js deleted file mode 100644 index aac145741f..0000000000 --- a/test/ToastBodySpec.js +++ /dev/null @@ -1,12 +0,0 @@ -import { mount } from 'enzyme'; - -import Toast from '../src/Toast'; - -describe('Toast.Body', () => { - it('will pass all props to the created div and renders its children', () => { - const content = Content; - mount( - {content}, - ).assertSingle('div.custom-class.toast-body>strong'); - }); -}); diff --git a/test/ToastBodySpec.tsx b/test/ToastBodySpec.tsx new file mode 100644 index 0000000000..a5b3d01fbf --- /dev/null +++ b/test/ToastBodySpec.tsx @@ -0,0 +1,16 @@ +import * as React from 'react'; +import { render } from '@testing-library/react'; +import Toast from '../src/Toast'; + +describe('Toast.Body', () => { + it('will pass all props to the created div and renders its children', () => { + const content = Content; + const { container } = render( + {content}, + ); + container.firstElementChild!.classList.contains('custom-class').should.be + .true; + container.firstElementChild!.classList.contains('toast-body').should.be + .true; + }); +}); diff --git a/test/ToastContainerSpec.js b/test/ToastContainerSpec.js deleted file mode 100644 index 6a199c424a..0000000000 --- a/test/ToastContainerSpec.js +++ /dev/null @@ -1,29 +0,0 @@ -import { mount } from 'enzyme'; - -import ToastContainer from '../src/ToastContainer'; - -const expectedClasses = { - 'top-start': '.position-absolute.top-0.start-0', - 'top-center': '.position-absolute.top-0.start-50.translate-middle-x', - 'top-end': '.position-absolute.top-0.end-0', - 'middle-start': '.position-absolute.top-50.start-0.translate-middle-y', - 'middle-center': '.position-absolute.top-50.start-50.translate-middle', - 'middle-end': '.position-absolute.top-50.end-0.translate-middle-y', - 'bottom-start': '.position-absolute.bottom-0.start-0', - 'bottom-center': '.position-absolute.bottom-0.start-50.translate-middle-x', - 'bottom-end': '.position-absolute.bottom-0.end-0', -}; - -describe('ToastContainer', () => { - it('should render a basic toast container', () => { - mount().assertSingle('.toast-container'); - }); - - Object.keys(expectedClasses).forEach((position) => { - it(`should render position=${position}`, () => { - mount().assertSingle( - expectedClasses[position], - ); - }); - }); -}); diff --git a/test/ToastContainerSpec.tsx b/test/ToastContainerSpec.tsx new file mode 100644 index 0000000000..670b144afd --- /dev/null +++ b/test/ToastContainerSpec.tsx @@ -0,0 +1,53 @@ +import { render } from '@testing-library/react'; +import ToastContainer, { ToastPosition } from '../src/ToastContainer'; + +const expectedClasses: Record> = { + 'top-start': ['position-absolute', 'top-0', 'start-0'], + 'top-center': [ + 'position-absolute', + 'top-0', + 'start-50', + 'translate-middle-x', + ], + 'top-end': ['position-absolute', 'top-0', 'end-0'], + 'middle-start': [ + 'position-absolute', + 'top-50', + 'start-0', + 'translate-middle-y', + ], + 'middle-center': [ + 'position-absolute', + 'top-50', + 'start-50', + 'translate-middle', + ], + 'middle-end': ['position-absolute', 'top-50', 'end-0', 'translate-middle-y'], + 'bottom-start': ['position-absolute', 'bottom-0', 'start-0'], + 'bottom-center': [ + 'position-absolute', + 'bottom-0', + 'start-50', + 'translate-middle-x', + ], + 'bottom-end': ['position-absolute', 'bottom-0', 'end-0'], +}; + +describe('ToastContainer', () => { + it('should render a basic toast container', () => { + const { container } = render(); + container.firstElementChild!.classList.contains('toast-container').should.be + .true; + }); + + Object.keys(expectedClasses).forEach((position: ToastPosition) => { + it(`should render position=${position}`, () => { + const { container } = render(); + expectedClasses[position].map( + (className) => + container.firstElementChild!.classList.contains(className).should.be + .true, + ); + }); + }); +}); diff --git a/test/ToastHeaderSpec.js b/test/ToastHeaderSpec.js deleted file mode 100644 index 0153a6556b..0000000000 --- a/test/ToastHeaderSpec.js +++ /dev/null @@ -1,25 +0,0 @@ -import { mount } from 'enzyme'; - -import Toast from '../src/Toast'; - -describe('Toast.Header', () => { - it('will pass all props to the created div and renders its children', () => { - mount( - - content - , - ).assertSingle('div.toast-header strong'); - }); - - it('should render close button variant', () => { - const wrapper = mount( - - content - , - ); - expect(wrapper.find('CloseButton').props()).to.have.property( - 'variant', - 'white', - ); - }); -}); diff --git a/test/ToastHeaderSpec.tsx b/test/ToastHeaderSpec.tsx new file mode 100644 index 0000000000..b6daed682e --- /dev/null +++ b/test/ToastHeaderSpec.tsx @@ -0,0 +1,28 @@ +import { render } from '@testing-library/react'; + +import Toast from '../src/Toast'; + +describe('Toast.Header', () => { + it('will pass all props to the created div and renders its children', () => { + const { container } = render( + + content + , + ); + container.firstElementChild!.tagName === 'div'; + container.firstElementChild!.firstElementChild!.tagName === 'strong'; + container.firstElementChild!.classList.contains('toast-header').should.be + .true; + }); + + it('should render close button variant', () => { + const { container } = render( + + content + , + ); + container + .firstElementChild!.getElementsByTagName('button')[0] + .classList.contains('btn-close-white').should.be.true; + }); +}); diff --git a/test/ToastSpec.js b/test/ToastSpec.tsx similarity index 60% rename from test/ToastSpec.js rename to test/ToastSpec.tsx index 96035eee44..d491917d4c 100644 --- a/test/ToastSpec.js +++ b/test/ToastSpec.tsx @@ -1,7 +1,20 @@ -import { mount } from 'enzyme'; +import { fireEvent, render } from '@testing-library/react'; +import sinon from 'sinon'; import Toast from '../src/Toast'; +const getToast = ({ + delay = 500, + onCloseSpy, + autohide = true, + show = true, +}) => ( + + header-content + body-content + +); + describe('', () => { let clock; @@ -14,51 +27,66 @@ describe('', () => { }); it('should apply bg prop', () => { - mount(Card).assertSingle('.toast.bg-primary'); + const { container } = render(Card); + container.firstElementChild!.classList.contains('bg-primary').should.be + .true; + container.firstElementChild!.classList.contains('toast').should.be.true; }); it('should render an entire toast', () => { - mount( + const { container } = render( , - ).assertSingle( - 'div.toast[className="fade toast show"][role="alert"][aria-live="assertive"][aria-atomic="true"]', + ); + ['fade', 'toast', 'show'].map((className) => + container.firstElementChild!.classList.contains(className), + ); + ( + [ + ['role', 'alert'], + ['aria-live', 'assertive'], + ['aria-atomic', true], + ] as const + ).map( + ([attrName, attrVal]) => + container.firstElementChild!.attributes.getNamedItem(attrName)! + .textContent === attrVal, ); }); it('should render without transition if animation is false', () => { - mount( + const { container } = render( , - ).assertSingle( - 'div.toast[className="toast show"][role="alert"][aria-live="assertive"][aria-atomic="true"]', + ); + + ['toast', 'show'].map((className) => + container.firstElementChild!.classList.contains(className), ); }); it('should trigger the onClose event after clicking on the close button', () => { const onCloseSpy = sinon.spy(); - mount( + const { container } = render( header-content body-content , - ) - .find('.toast-header') - .at(0) - .find('button') - .simulate('click'); - + ); + fireEvent.click( + container.firstElementChild!.getElementsByTagName('button')[0], + ); expect(onCloseSpy).to.have.been.calledOnce; }); it('should trigger the onClose event after the autohide delay', () => { const onCloseSpy = sinon.spy(); - mount( + render( header-content body-content @@ -70,7 +98,7 @@ describe('', () => { it('should not trigger the onClose event if autohide is not set', () => { const onCloseSpy = sinon.spy(); - mount( + render( header-content body-content @@ -82,30 +110,26 @@ describe('', () => { it('should clearTimeout after unmount', () => { const onCloseSpy = sinon.spy(); - const wrapper = mount( + const { unmount } = render( header-content body-content , ); - wrapper.unmount(); + unmount(); clock.tick(1000); expect(onCloseSpy).not.to.have.been.called; }); it('should not reset autohide timer when element re-renders with same props', () => { const onCloseSpy = sinon.spy(); - const wrapper = mount( - - header-content - body-content - , - ); + const toast = getToast({ onCloseSpy }); + const { rerender } = render(toast); clock.tick(250); // Trigger render with no props changes. - wrapper.setProps({}); + rerender(toast); clock.tick(300); expect(onCloseSpy).to.have.been.calledOnce; @@ -113,16 +137,11 @@ describe('', () => { it('should not reset autohide timer when delay is changed', () => { const onCloseSpy = sinon.spy(); - const wrapper = mount( - - header-content - body-content - , - ); + const { rerender } = render(getToast({ delay: 500, onCloseSpy })); clock.tick(250); - wrapper.setProps({ delay: 10000 }); + rerender(getToast({ delay: 10000, onCloseSpy })); clock.tick(300); expect(onCloseSpy).to.have.been.calledOnce; @@ -131,16 +150,12 @@ describe('', () => { it('should not reset autohide timer when onClosed is changed', () => { const onCloseSpy = sinon.spy(); const onCloseSpy2 = sinon.spy(); - const wrapper = mount( - - header-content - body-content - , - ); + + const { rerender } = render(getToast({ onCloseSpy })); clock.tick(250); - wrapper.setProps({ onClose: onCloseSpy2 }); + rerender(getToast({ onCloseSpy: onCloseSpy2 })); clock.tick(300); expect(onCloseSpy).not.to.have.been.called; @@ -149,16 +164,11 @@ describe('', () => { it('should not call onClose if autohide is changed from true to false', () => { const onCloseSpy = sinon.spy(); - const wrapper = mount( - - header-content - body-content - , - ); + const { rerender } = render(getToast({ onCloseSpy, autohide: true })); clock.tick(250); - wrapper.setProps({ autohide: false }); + rerender(getToast({ onCloseSpy, autohide: false })); clock.tick(300); expect(onCloseSpy).not.to.have.been.called; @@ -166,27 +176,23 @@ describe('', () => { it('should not call onClose if show is changed from true to false', () => { const onCloseSpy = sinon.spy(); - const wrapper = mount( - - header-content - body-content - , - ); - - clock.tick(250); + const { rerender } = render(getToast({ show: true, onCloseSpy })); + clock.tick(100); - wrapper.setProps({ show: false }); + rerender(getToast({ show: false, onCloseSpy })); clock.tick(300); expect(onCloseSpy).not.to.have.been.called; }); it('should render with bsPrefix', () => { - mount( + const { container } = render( , - ).assertSingle('div.my-toast'); + ); + container.firstElementChild!.tagName === 'div'; + container.firstElementChild!.classList.contains('my-toast'); }); });