From a322886710cfece7b4f9a9cbba7b2af0c1df9b87 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Tue, 3 May 2022 20:36:24 +0200 Subject: [PATCH] Migrated MainHeader test to react testing library --- src/common/MainHeader.tsx | 4 +- src/common/services/provideServices.ts | 2 +- test/common/MainHeader.test.tsx | 81 +++++++++++++------------- 3 files changed, 41 insertions(+), 46 deletions(-) diff --git a/src/common/MainHeader.tsx b/src/common/MainHeader.tsx index 90502a32..29c6f41c 100644 --- a/src/common/MainHeader.tsx +++ b/src/common/MainHeader.tsx @@ -8,7 +8,7 @@ import { useToggle } from '../utils/helpers/hooks'; import { ShlinkLogo } from './img/ShlinkLogo'; import './MainHeader.scss'; -const MainHeader = (ServersDropdown: FC) => () => { +export const MainHeader = (ServersDropdown: FC) => () => { const [isOpen, toggleOpen, , close] = useToggle(); const location = useLocation(); const { pathname } = location; @@ -41,5 +41,3 @@ const MainHeader = (ServersDropdown: FC) => () => { ); }; - -export default MainHeader; diff --git a/src/common/services/provideServices.ts b/src/common/services/provideServices.ts index 5bd8019a..e242f875 100644 --- a/src/common/services/provideServices.ts +++ b/src/common/services/provideServices.ts @@ -1,7 +1,7 @@ import axios from 'axios'; import Bottle from 'bottlejs'; import ScrollToTop from '../ScrollToTop'; -import MainHeader from '../MainHeader'; +import { MainHeader } from '../MainHeader'; import { Home } from '../Home'; import MenuLayout from '../MenuLayout'; import AsideMenu from '../AsideMenu'; diff --git a/test/common/MainHeader.test.tsx b/test/common/MainHeader.test.tsx index be5fe590..9d2a6c78 100644 --- a/test/common/MainHeader.test.tsx +++ b/test/common/MainHeader.test.tsx @@ -1,34 +1,24 @@ -import { shallow, ShallowWrapper } from 'enzyme'; -import { useLocation } from 'react-router-dom'; -import { Collapse, NavbarToggler, NavLink } from 'reactstrap'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import createMainHeader from '../../src/common/MainHeader'; - -jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useLocation: jest.fn().mockReturnValue({}), -})); +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { Router } from 'react-router-dom'; +import { createMemoryHistory } from 'history'; +import { MainHeader as createMainHeader } from '../../src/common/MainHeader'; describe('', () => { - const ServersDropdown = () => null; - const MainHeader = createMainHeader(ServersDropdown); - let wrapper: ShallowWrapper; + const MainHeader = createMainHeader(() => <>ServersDropdown); + const setUp = (pathname = '') => { + const history = createMemoryHistory(); + history.push(pathname); - const createWrapper = (pathname = '') => { - (useLocation as any).mockReturnValue({ pathname }); - - wrapper = shallow(); - - return wrapper; + return render( + + + , + ); }; - afterEach(jest.clearAllMocks); - afterEach(() => wrapper?.unmount()); - it('renders ServersDropdown', () => { - const wrapper = createWrapper(); - - expect(wrapper.find(ServersDropdown)).toHaveLength(1); + setUp(); + expect(screen.getByText('ServersDropdown')).toBeInTheDocument(); }); it.each([ @@ -38,31 +28,38 @@ describe('', () => { ['/settings/foo', true], ['/settings/bar', true], ])('sets link to settings as active only when current path is settings', (currentPath, isActive) => { - const wrapper = createWrapper(currentPath); - const settingsLink = wrapper.find(NavLink); + setUp(currentPath); - expect(settingsLink.prop('active')).toEqual(isActive); + if (isActive) { + expect(screen.getByText(/Settings$/).getAttribute('class')).toContain('active'); + } else { + expect(screen.getByText(/Settings$/).getAttribute('class')).not.toContain('active'); + } }); it('renders expected class based on the nav bar state', () => { - const wrapper = createWrapper(); + setUp(); - expect(wrapper.find(NavbarToggler).find(FontAwesomeIcon).prop('className')).toEqual('main-header__toggle-icon'); - wrapper.find(NavbarToggler).simulate('click'); - expect(wrapper.find(NavbarToggler).find(FontAwesomeIcon).prop('className')).toEqual( - 'main-header__toggle-icon main-header__toggle-icon--opened', + const toggle = screen.getByLabelText('Toggle navigation'); + const icon = toggle.firstChild; + + expect(icon).toHaveAttribute('class', expect.stringMatching(/main-header__toggle-icon$/)); + fireEvent.click(toggle); + expect(icon).toHaveAttribute( + 'class', + expect.stringMatching(/main-header__toggle-icon main-header__toggle-icon--opened$/), ); - wrapper.find(NavbarToggler).simulate('click'); - expect(wrapper.find(NavbarToggler).find(FontAwesomeIcon).prop('className')).toEqual('main-header__toggle-icon'); + fireEvent.click(toggle); + expect(icon).toHaveAttribute('class', expect.stringMatching(/main-header__toggle-icon$/)); }); - it('opens Collapse when clicking toggle', () => { - const wrapper = createWrapper(); + it('opens Collapse when clicking toggle', async () => { + const { container } = setUp(); + const collapse = container.querySelector('.collapse'); + const toggle = screen.getByLabelText('Toggle navigation'); - expect(wrapper.find(Collapse).prop('isOpen')).toEqual(false); - wrapper.find(NavbarToggler).simulate('click'); - expect(wrapper.find(Collapse).prop('isOpen')).toEqual(true); - wrapper.find(NavbarToggler).simulate('click'); - expect(wrapper.find(Collapse).prop('isOpen')).toEqual(false); + expect(collapse).not.toHaveAttribute('class', expect.stringContaining('show')); + fireEvent.click(toggle); + await waitFor(() => expect(collapse).toHaveAttribute('class', expect.stringContaining('show'))); }); });