From a90c3da7b6f4e9357272325a668e573a9917bb1e Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Mon, 6 Jun 2022 22:23:21 +0200 Subject: [PATCH 1/2] Migrated RealTimeUpdatesSettings test to react testing library --- src/settings/RealTimeUpdatesSettings.tsx | 80 ++++++++------ .../settings/RealTimeUpdatesSettings.test.tsx | 104 ++++++++---------- 2 files changed, 87 insertions(+), 97 deletions(-) diff --git a/src/settings/RealTimeUpdatesSettings.tsx b/src/settings/RealTimeUpdatesSettings.tsx index a4218b5a..f587baa8 100644 --- a/src/settings/RealTimeUpdatesSettings.tsx +++ b/src/settings/RealTimeUpdatesSettings.tsx @@ -5,6 +5,7 @@ import { SimpleCard } from '../utils/SimpleCard'; import { FormText } from '../utils/forms/FormText'; import { LabeledFormGroup } from '../utils/forms/LabeledFormGroup'; import { Settings } from './reducers/settings'; +import { useDomId } from '../utils/helpers/hooks'; interface RealTimeUpdatesProps { settings: Settings; @@ -16,39 +17,46 @@ const intervalValue = (interval?: number) => (!interval ? '' : `${interval}`); export const RealTimeUpdatesSettings = ( { settings: { realTimeUpdates }, toggleRealTimeUpdates, setRealTimeUpdatesInterval }: RealTimeUpdatesProps, -) => ( - - - - Enable or disable real-time updates. - - Real-time updates are currently being {realTimeUpdates.enabled ? 'processed' : 'ignored'}. - - - - - setRealTimeUpdatesInterval(Number(target.value))} - /> - {realTimeUpdates.enabled && ( - - {realTimeUpdates.interval !== undefined && realTimeUpdates.interval > 0 && ( - - Updates will be reflected in the UI every {realTimeUpdates.interval} minute{realTimeUpdates.interval > 1 && 's'}. - - )} - {!realTimeUpdates.interval && 'Updates will be reflected in the UI as soon as they happen.'} - - )} - - -); +) => { + const inputId = useDomId(); + + return ( + + + + Enable or disable real-time updates. + + Real-time updates are currently being {realTimeUpdates.enabled ? 'processed' : 'ignored'}. + + + + + setRealTimeUpdatesInterval(Number(target.value))} + /> + {realTimeUpdates.enabled && ( + + {realTimeUpdates.interval !== undefined && realTimeUpdates.interval > 0 && ( + + Updates will be reflected in the UI + every {realTimeUpdates.interval} minute{realTimeUpdates.interval > 1 && 's'}. + + )} + {!realTimeUpdates.interval && 'Updates will be reflected in the UI as soon as they happen.'} + + )} + + + ); +}; diff --git a/test/settings/RealTimeUpdatesSettings.test.tsx b/test/settings/RealTimeUpdatesSettings.test.tsx index 170c1de9..947e27e4 100644 --- a/test/settings/RealTimeUpdatesSettings.test.tsx +++ b/test/settings/RealTimeUpdatesSettings.test.tsx @@ -1,64 +1,54 @@ -import { shallow, ShallowWrapper } from 'enzyme'; +import userEvent from '@testing-library/user-event'; +import { render, screen } from '@testing-library/react'; import { Mock } from 'ts-mockery'; -import { Input } from 'reactstrap'; -import { FormText } from '../../src/utils/forms/FormText'; import { RealTimeUpdatesSettings as RealTimeUpdatesSettingsOptions, Settings, } from '../../src/settings/reducers/settings'; import { RealTimeUpdatesSettings } from '../../src/settings/RealTimeUpdatesSettings'; -import { ToggleSwitch } from '../../src/utils/ToggleSwitch'; -import { LabeledFormGroup } from '../../src/utils/forms/LabeledFormGroup'; describe('', () => { const toggleRealTimeUpdates = jest.fn(); const setRealTimeUpdatesInterval = jest.fn(); - let wrapper: ShallowWrapper; - const createWrapper = (realTimeUpdates: Partial = {}) => { - const settings = Mock.of({ realTimeUpdates }); - - wrapper = shallow( + const setUp = (realTimeUpdates: Partial = {}) => ({ + user: userEvent.setup(), + ...render( ({ realTimeUpdates })} toggleRealTimeUpdates={toggleRealTimeUpdates} setRealTimeUpdatesInterval={setRealTimeUpdatesInterval} />, - ); - - return wrapper; - }; + ), + }); afterEach(jest.clearAllMocks); - afterEach(() => wrapper?.unmount()); it('renders enabled real time updates as expected', () => { - const wrapper = createWrapper({ enabled: true }); - const toggle = wrapper.find(ToggleSwitch); - const label = wrapper.find(LabeledFormGroup); - const input = wrapper.find(Input); - const formText = wrapper.find(FormText); + setUp({ enabled: true }); - expect(toggle.prop('checked')).toEqual(true); - expect(toggle.html()).toContain('processed'); - expect(toggle.html()).not.toContain('ignored'); - expect(label.prop('labelClassName')).not.toContain('text-muted'); - expect(input.prop('disabled')).toEqual(false); - expect(formText).toHaveLength(2); + expect(screen.getByLabelText(/^Enable or disable real-time updates./)).toBeChecked(); + expect(screen.getByText(/^Real-time updates are currently being/)).toHaveTextContent('processed'); + expect(screen.getByText(/^Real-time updates are currently being/)).not.toHaveTextContent('ignored'); + expect(screen.getByText('Real-time updates frequency (in minutes):')).not.toHaveAttribute( + 'class', + expect.stringContaining('text-muted'), + ); + expect(screen.getByLabelText('Real-time updates frequency (in minutes):')).not.toHaveAttribute('disabled'); + expect(screen.getByText('Updates will be reflected in the UI as soon as they happen.')).toBeInTheDocument(); }); it('renders disabled real time updates as expected', () => { - const wrapper = createWrapper({ enabled: false }); - const toggle = wrapper.find(ToggleSwitch); - const label = wrapper.find(LabeledFormGroup); - const input = wrapper.find(Input); - const formText = wrapper.find(FormText); + setUp({ enabled: false }); - expect(toggle.prop('checked')).toEqual(false); - expect(toggle.html()).not.toContain('processed'); - expect(toggle.html()).toContain('ignored'); - expect(label.prop('labelClassName')).toContain('text-muted'); - expect(input.prop('disabled')).toEqual(true); - expect(formText).toHaveLength(1); + expect(screen.getByLabelText(/^Enable or disable real-time updates./)).not.toBeChecked(); + expect(screen.getByText(/^Real-time updates are currently being/)).not.toHaveTextContent('processed'); + expect(screen.getByText(/^Real-time updates are currently being/)).toHaveTextContent('ignored'); + expect(screen.getByText('Real-time updates frequency (in minutes):')).toHaveAttribute( + 'class', + expect.stringContaining('text-muted'), + ); + expect(screen.getByLabelText('Real-time updates frequency (in minutes):')).toHaveAttribute('disabled'); + expect(screen.queryByText('Updates will be reflected in the UI as soon as they happen.')).not.toBeInTheDocument(); }); it.each([ @@ -67,43 +57,35 @@ describe('', () => { [10, 'minutes'], [100, 'minutes'], ])('shows expected children when interval is greater than 0', (interval, minutesWord) => { - const wrapper = createWrapper({ enabled: true, interval }); - const span = wrapper.find('span'); - const input = wrapper.find(Input); + setUp({ enabled: true, interval }); - expect(span).toHaveLength(1); - expect(span.html()).toEqual( - `Updates will be reflected in the UI every ${interval} ${minutesWord}.`, + expect(screen.getByText(/^Updates will be reflected in the UI every/)).toHaveTextContent( + `${interval} ${minutesWord}`, ); - expect(input.prop('value')).toEqual(`${interval}`); + expect(screen.getByLabelText('Real-time updates frequency (in minutes):')).toHaveValue(interval); + expect(screen.queryByText('Updates will be reflected in the UI as soon as they happen.')).not.toBeInTheDocument(); }); it.each([[undefined], [0]])('shows expected children when interval is 0 or undefined', (interval) => { - const wrapper = createWrapper({ enabled: true, interval }); - const span = wrapper.find('span'); - const formText = wrapper.find(FormText).at(1); - const input = wrapper.find(Input); + setUp({ enabled: true, interval }); - expect(span).toHaveLength(0); - expect(formText.html()).toContain('Updates will be reflected in the UI as soon as they happen.'); - expect(input.prop('value')).toEqual(''); + expect(screen.queryByText(/^Updates will be reflected in the UI every/)).not.toBeInTheDocument(); + expect(screen.getByText('Updates will be reflected in the UI as soon as they happen.')).toBeInTheDocument(); }); - it('updates real time updates on input change', () => { - const wrapper = createWrapper(); - const input = wrapper.find(Input); + it('updates real time updates when typing on input', async () => { + const { user } = setUp({ enabled: true }); expect(setRealTimeUpdatesInterval).not.toHaveBeenCalled(); - input.simulate('change', { target: { value: '10' } }); - expect(setRealTimeUpdatesInterval).toHaveBeenCalledWith(10); + await user.type(screen.getByLabelText('Real-time updates frequency (in minutes):'), '5'); + expect(setRealTimeUpdatesInterval).toHaveBeenCalledWith(5); }); - it('toggles real time updates on switch change', () => { - const wrapper = createWrapper(); - const toggle = wrapper.find(ToggleSwitch); + it('toggles real time updates on switch change', async () => { + const { user } = setUp({ enabled: true }); expect(toggleRealTimeUpdates).not.toHaveBeenCalled(); - toggle.simulate('change'); + await user.click(screen.getByText(/^Enable or disable real-time updates./)); expect(toggleRealTimeUpdates).toHaveBeenCalled(); }); }); From 4a70e4ecd36857b53fbaf9db2d2f6cabcdaac1dc Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Mon, 6 Jun 2022 22:38:07 +0200 Subject: [PATCH 2/2] Migrated ShortUrlsListSettings test to react testing library --- test/settings/ShortUrlsListSettings.test.tsx | 55 ++++++++------------ 1 file changed, 23 insertions(+), 32 deletions(-) diff --git a/test/settings/ShortUrlsListSettings.test.tsx b/test/settings/ShortUrlsListSettings.test.tsx index c167ea4e..104fd6f3 100644 --- a/test/settings/ShortUrlsListSettings.test.tsx +++ b/test/settings/ShortUrlsListSettings.test.tsx @@ -1,52 +1,43 @@ -import { shallow, ShallowWrapper } from 'enzyme'; +import userEvent from '@testing-library/user-event'; +import { render, screen } from '@testing-library/react'; import { Mock } from 'ts-mockery'; -import { - DEFAULT_SHORT_URLS_ORDERING, - Settings, - ShortUrlsListSettings as ShortUrlsSettings, -} from '../../src/settings/reducers/settings'; +import { Settings, ShortUrlsListSettings as ShortUrlsSettings } from '../../src/settings/reducers/settings'; import { ShortUrlsListSettings } from '../../src/settings/ShortUrlsListSettings'; -import { OrderingDropdown } from '../../src/utils/OrderingDropdown'; import { ShortUrlsOrder } from '../../src/short-urls/data'; describe('', () => { - let wrapper: ShallowWrapper; const setSettings = jest.fn(); - const createWrapper = (shortUrlsList?: ShortUrlsSettings) => { - wrapper = shallow( + const setUp = (shortUrlsList?: ShortUrlsSettings) => ({ + user: userEvent.setup(), + ...render( ({ shortUrlsList })} setShortUrlsListSettings={setSettings} />, - ); + ), + }); - return wrapper; - }; - - afterEach(() => wrapper?.unmount()); afterEach(jest.clearAllMocks); it.each([ - [undefined, DEFAULT_SHORT_URLS_ORDERING], - [{}, DEFAULT_SHORT_URLS_ORDERING], - [{ defaultOrdering: {} }, {}], - [{ defaultOrdering: { field: 'longUrl', dir: 'DESC' } as ShortUrlsOrder }, { field: 'longUrl', dir: 'DESC' }], - [{ defaultOrdering: { field: 'visits', dir: 'ASC' } as ShortUrlsOrder }, { field: 'visits', dir: 'ASC' }], + [undefined, 'Order by: Created at - DESC'], + [{}, 'Order by: Created at - DESC'], + [{ defaultOrdering: {} }, 'Order by...'], + [{ defaultOrdering: { field: 'longUrl', dir: 'DESC' } as ShortUrlsOrder }, 'Order by: Long URL - DESC'], + [{ defaultOrdering: { field: 'visits', dir: 'ASC' } as ShortUrlsOrder }, 'Order by: Visits - ASC'], ])('shows expected ordering', (shortUrlsList, expectedOrder) => { - const wrapper = createWrapper(shortUrlsList); - const dropdown = wrapper.find(OrderingDropdown); - - expect(dropdown.prop('order')).toEqual(expectedOrder); + setUp(shortUrlsList); + expect(screen.getByRole('button')).toHaveTextContent(expectedOrder); }); it.each([ - [undefined, undefined], - ['longUrl', 'ASC'], - ['visits', undefined], - ['title', 'DESC'], - ])('invokes setSettings when ordering changes', (field, dir) => { - const wrapper = createWrapper(); - const dropdown = wrapper.find(OrderingDropdown); + ['Clear selection', undefined, undefined], + ['Long URL', 'longUrl', 'ASC'], + ['Visits', 'visits', 'ASC'], + ['Title', 'title', 'ASC'], + ])('invokes setSettings when ordering changes', async (name, field, dir) => { + const { user } = setUp(); expect(setSettings).not.toHaveBeenCalled(); - dropdown.simulate('change', field, dir); + await user.click(screen.getByRole('button')); + await user.click(screen.getByRole('menuitem', { name })); expect(setSettings).toHaveBeenCalledWith({ defaultOrdering: { field, dir } }); }); });