From a012d6206f1933380b7d677f15415dc576377721 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 5 Jun 2022 11:06:26 +0200 Subject: [PATCH] Migrated ImportServersBtn test to react testing library --- src/servers/helpers/ImportServersBtn.tsx | 18 ++-- .../servers/helpers/ImportServersBtn.test.tsx | 95 ++++++++----------- 2 files changed, 51 insertions(+), 62 deletions(-) diff --git a/src/servers/helpers/ImportServersBtn.tsx b/src/servers/helpers/ImportServersBtn.tsx index a5673623..a852156a 100644 --- a/src/servers/helpers/ImportServersBtn.tsx +++ b/src/servers/helpers/ImportServersBtn.tsx @@ -1,4 +1,4 @@ -import { useRef, RefObject, ChangeEvent, MutableRefObject, useState, useEffect, FC, PropsWithChildren } from 'react'; +import { useRef, ChangeEvent, useState, useEffect, FC, PropsWithChildren } from 'react'; import { Button, UncontrolledTooltip } from 'reactstrap'; import { complement, pipe } from 'ramda'; import { faFileUpload as importIcon } from '@fortawesome/free-solid-svg-icons'; @@ -9,8 +9,6 @@ import { ServerData, ServersMap } from '../data'; import { DuplicatedServersModal } from './DuplicatedServersModal'; import './ImportServersBtn.scss'; -type Ref = RefObject | MutableRefObject; - export type ImportServersBtnProps = PropsWithChildren<{ onImport?: () => void; onImportError?: (error: Error) => void; @@ -21,7 +19,6 @@ export type ImportServersBtnProps = PropsWithChildren<{ interface ImportServersBtnConnectProps extends ImportServersBtnProps { createServers: (servers: ServerData[]) => void; servers: ServersMap; - fileRef: Ref; } const serversFiltering = (servers: ServerData[]) => @@ -30,14 +27,13 @@ const serversFiltering = (servers: ServerData[]) => export const ImportServersBtn = ({ importServersFromFile }: ServersImporter): FC => ({ createServers, servers, - fileRef, children, onImport = () => {}, onImportError = () => {}, tooltipPlacement = 'bottom', className = '', }) => { - const ref = fileRef ?? useRef(); + const ref = useRef(); const [serversToCreate, setServersToCreate] = useState(); const [duplicatedServers, setDuplicatedServers] = useState([]); const [isModalOpen,, showModal, hideModal] = useToggle(); @@ -78,7 +74,15 @@ export const ImportServersBtn = ({ importServersFromFile }: ServersImporter): FC You can create servers by importing a CSV file with columns name, apiKey and url. - + { + ref.current = el ?? undefined; + }} + onChange={onFile} + /> ', () => { - let wrapper: ShallowWrapper; const onImportMock = jest.fn(); const createServersMock = jest.fn(); const importServersFromFile = jest.fn().mockResolvedValue([]); const serversImporterMock = Mock.of({ importServersFromFile }); - const click = jest.fn(); - const fileRef = { current: Mock.of({ click }) }; const ImportServersBtn = createImportServersBtn(serversImporterMock); - const createWrapper = (props: Partial = {}) => { - wrapper = shallow( + const setUp = (props: Partial = {}, servers: ServersMap = {}) => ({ + user: userEvent.setup(), + ...render( , - ); - - return wrapper; - }; + ), + }); afterEach(jest.clearAllMocks); - afterEach(() => wrapper.unmount()); - it('renders a button, a tooltip and a file input', () => { - const wrapper = createWrapper(); + it('shows tooltip on button hover', async () => { + const { user } = setUp(); - expect(wrapper.find('#importBtn')).toHaveLength(1); - expect(wrapper.find(UncontrolledTooltip)).toHaveLength(1); - expect(wrapper.find('.import-servers-btn__csv-select')).toHaveLength(1); + expect(screen.queryByText(/^You can create servers by importing a CSV file/)).not.toBeInTheDocument(); + await user.hover(screen.getByRole('button')); + await waitFor( + () => expect(screen.getByText(/^You can create servers by importing a CSV file/)).toBeInTheDocument(), + ); }); it.each([ @@ -48,53 +43,43 @@ describe('', () => { ['foo', 'foo'], ['bar', 'bar'], ])('allows a class name to be provided', (providedClassName, expectedClassName) => { - const wrapper = createWrapper({ className: providedClassName }); - - expect(wrapper.find('#importBtn').prop('className')).toEqual(expectedClassName); + setUp({ className: providedClassName }); + expect(screen.getByRole('button')).toHaveAttribute('class', expect.stringContaining(expectedClassName)); }); it.each([ - [undefined, true], - ['foo', false], - ['bar', false], - ])('has expected text', (children, expectToHaveDefaultText) => { - const wrapper = createWrapper({ children }); - - if (expectToHaveDefaultText) { - expect(wrapper.find('#importBtn').html()).toContain('Import from file'); - } else { - expect(wrapper.find('#importBtn').html()).toContain(children); - expect(wrapper.find('#importBtn').html()).not.toContain('Import from file'); - } - }); - - it('triggers click on file ref when button is clicked', () => { - const wrapper = createWrapper(); - const btn = wrapper.find('#importBtn'); - - btn.simulate('click'); - - expect(click).toHaveBeenCalledTimes(1); + [undefined, 'Import from file'], + ['foo', 'foo'], + ['bar', 'bar'], + ])('has expected text', (children, expectedText) => { + setUp({ children }); + expect(screen.getByRole('button')).toHaveTextContent(expectedText); }); it('imports servers when file input changes', async () => { - const wrapper = createWrapper(); - const file = wrapper.find('.import-servers-btn__csv-select'); - - await file.simulate('change', { target: { files: [''] } }); + const { container } = setUp(); + const input = container.querySelector('[type=file]'); + input && fireEvent.change(input, { target: { files: [''] } }); expect(importServersFromFile).toHaveBeenCalledTimes(1); }); it.each([ - ['discard'], - ['save'], - ])('invokes callback in DuplicatedServersModal events', (event) => { - const wrapper = createWrapper(); + ['Save anyway', true], + ['Discard', false], + ])('creates expected servers depending on selected option in modal', async (btnName, savesDuplicatedServers) => { + const existingServer = Mock.of({ id: 'abc', url: 'existingUrl', apiKey: 'existingApiKey' }); + const newServer = Mock.of({ url: 'newUrl', apiKey: 'newApiKey' }); + const { container, user } = setUp({}, { abc: existingServer }); + const input = container.querySelector('[type=file]'); + importServersFromFile.mockResolvedValue([existingServer, newServer]); - wrapper.find(DuplicatedServersModal).simulate(event); + expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); + input && fireEvent.change(input, { target: { files: [''] } }); + await waitFor(() => expect(screen.getByRole('dialog')).toBeInTheDocument()); + await user.click(screen.getByRole('button', { name: btnName })); - expect(createServersMock).toHaveBeenCalledTimes(1); + expect(createServersMock).toHaveBeenCalledWith(savesDuplicatedServers ? [existingServer, newServer] : [newServer]); expect(onImportMock).toHaveBeenCalledTimes(1); }); });