Updated ShortUrlsFilteringBar test

This commit is contained in:
Alejandro Celaya 2022-05-14 16:36:45 +02:00
parent 30aeba0af2
commit ed366fa4cc
4 changed files with 82 additions and 102 deletions

View file

@ -60,7 +60,7 @@ const ShortUrlsFilteringBar = (
<TagsSelector allowNew={false} placeholder="With tags..." selectedTags={tags} onChange={changeTagSelection} /> <TagsSelector allowNew={false} placeholder="With tags..." selectedTags={tags} onChange={changeTagSelection} />
{canChangeTagsMode && tags.length > 1 && ( {canChangeTagsMode && tags.length > 1 && (
<> <>
<Button outline color="secondary" onClick={toggleTagsMode} id="tagsModeBtn"> <Button outline color="secondary" onClick={toggleTagsMode} id="tagsModeBtn" aria-label="Change tags mode">
<FontAwesomeIcon className="short-urls-filtering-bar__tags-icon" icon={tagsMode === 'all' ? faTags : faTag} /> <FontAwesomeIcon className="short-urls-filtering-bar__tags-icon" icon={tagsMode === 'all' ? faTags : faTag} />
</Button> </Button>
<UncontrolledTooltip target="tagsModeBtn" placement="left"> <UncontrolledTooltip target="tagsModeBtn" placement="left">

View file

@ -20,7 +20,6 @@ export function OrderingDropdown<T extends string = string>(
) { ) {
const handleItemClick = (fieldKey: T) => () => { const handleItemClick = (fieldKey: T) => () => {
const newOrderDir = determineOrderDir(fieldKey, order.field, order.dir); const newOrderDir = determineOrderDir(fieldKey, order.field, order.dir);
onChange(newOrderDir ? fieldKey : undefined, newOrderDir); onChange(newOrderDir ? fieldKey : undefined, newOrderDir);
}; };

View file

@ -1,5 +1,6 @@
import DateInput from '../DateInput'; import DateInput from '../DateInput';
import { DateRange } from './types'; import { DateRange } from './types';
import { endOfDay } from 'date-fns';
interface DateRangeRowProps extends DateRange { interface DateRangeRowProps extends DateRange {
onStartDateChange: (date: Date | null) => void; onStartDateChange: (date: Date | null) => void;
@ -29,7 +30,7 @@ const DateRangeRow = (
isClearable isClearable
minDate={startDate ?? undefined} minDate={startDate ?? undefined}
disabled={disabled} disabled={disabled}
onChange={onEndDateChange} onChange={(date) => onEndDateChange(date && endOfDay(date))}
/> />
</div> </div>
</div> </div>

View file

@ -1,156 +1,136 @@
import { shallow, ShallowWrapper } from 'enzyme'; import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Mock } from 'ts-mockery'; import { Mock } from 'ts-mockery';
import { formatISO } from 'date-fns'; import { endOfDay, formatISO, startOfDay } from 'date-fns';
import { useLocation, useNavigate } from 'react-router-dom'; import { MemoryRouter, useLocation, useNavigate } from 'react-router-dom';
import filteringBarCreator from '../../src/short-urls/ShortUrlsFilteringBar'; import filteringBarCreator from '../../src/short-urls/ShortUrlsFilteringBar';
import SearchField from '../../src/utils/SearchField';
import Tag from '../../src/tags/helpers/Tag';
import { DateRangeSelector } from '../../src/utils/dates/DateRangeSelector';
import { ReachableServer, SelectedServer } from '../../src/servers/data'; import { ReachableServer, SelectedServer } from '../../src/servers/data';
import { OrderingDropdown } from '../../src/utils/OrderingDropdown'; import { DateRange } from '../../src/utils/dates/types';
import { formatDate } from '../../src/utils/helpers/date';
jest.mock('react-router-dom', () => ({ jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'), ...jest.requireActual('react-router-dom'),
useNavigate: jest.fn(),
useParams: jest.fn().mockReturnValue({ serverId: '1' }), useParams: jest.fn().mockReturnValue({ serverId: '1' }),
useNavigate: jest.fn(),
useLocation: jest.fn().mockReturnValue({}), useLocation: jest.fn().mockReturnValue({}),
})); }));
const TooltipToggleSwitch = () => null; // TODO Drop this!
describe('<ShortUrlsFilteringBar />', () => { describe('<ShortUrlsFilteringBar />', () => {
let wrapper: ShallowWrapper; const ShortUrlsFilteringBar = filteringBarCreator(() => <>ExportShortUrlsBtn</>, () => <>TagsSelector</>);
const ExportShortUrlsBtn = () => null;
const ShortUrlsFilteringBar = filteringBarCreator(ExportShortUrlsBtn, () => null);
const navigate = jest.fn(); const navigate = jest.fn();
const handleOrderBy = jest.fn(); const handleOrderBy = jest.fn();
const now = new Date(); const now = new Date();
const createWrapper = (search = '', selectedServer?: SelectedServer) => { const setUp = (search = '', selectedServer?: SelectedServer) => {
(useLocation as any).mockReturnValue({ search }); (useLocation as any).mockReturnValue({ search });
(useNavigate as any).mockReturnValue(navigate); (useNavigate as any).mockReturnValue(navigate);
wrapper = shallow( return {
user: userEvent.setup(),
...render(
<MemoryRouter>
<ShortUrlsFilteringBar <ShortUrlsFilteringBar
selectedServer={selectedServer ?? Mock.all<SelectedServer>()} selectedServer={selectedServer ?? Mock.all<SelectedServer>()}
order={{}} order={{}}
handleOrderBy={handleOrderBy} handleOrderBy={handleOrderBy}
/>, />
); </MemoryRouter>,
),
return wrapper; };
}; };
afterEach(jest.clearAllMocks); afterEach(jest.clearAllMocks);
afterEach(() => wrapper?.unmount());
it('renders expected children components', () => { it('renders expected children components', () => {
const wrapper = createWrapper(); setUp();
expect(wrapper.find(SearchField)).toHaveLength(1); expect(screen.getByText('ExportShortUrlsBtn')).toBeInTheDocument();
expect(wrapper.find(DateRangeSelector)).toHaveLength(1); expect(screen.getByText('TagsSelector')).toBeInTheDocument();
expect(wrapper.find(OrderingDropdown)).toHaveLength(1); });
expect(wrapper.find(ExportShortUrlsBtn)).toHaveLength(1);
it('redirects to first page when search field changes', async () => {
const { user } = setUp();
expect(navigate).not.toHaveBeenCalled();
await user.type(screen.getByPlaceholderText('Search...'), 'search-term');
await waitFor(() => expect(navigate).toHaveBeenCalledWith('/server/1/list-short-urls/1?search=search-term'));
}); });
it.each([ it.each([
['tags=foo,bar,baz', 3], [{ startDate: now } as DateRange, `startDate=${encodeURIComponent(formatISO(startOfDay(now)))}`],
['tags=foo,baz', 2], [{ endDate: now } as DateRange, `endDate=${encodeURIComponent(formatISO(endOfDay(now)))}`],
['', 0],
['foo=bar', 0],
])('renders the proper amount of tags', (search, expectedTagComps) => {
const wrapper = createWrapper(search);
expect(wrapper.find(Tag)).toHaveLength(expectedTagComps);
});
it('redirects to first page when search field changes', () => {
const wrapper = createWrapper();
const searchField = wrapper.find(SearchField);
expect(navigate).not.toHaveBeenCalled();
searchField.simulate('change', 'search-term');
expect(navigate).toHaveBeenCalledWith('/server/1/list-short-urls/1?search=search-term');
});
it('redirects to first page when a tag is removed', () => {
const wrapper = createWrapper('tags=foo,bar');
const tag = wrapper.find(Tag).first();
expect(navigate).not.toHaveBeenCalled();
tag.simulate('close');
expect(navigate).toHaveBeenCalledWith('/server/1/list-short-urls/1?tags=bar');
});
it.each([
[{ startDate: now }, `startDate=${encodeURIComponent(formatISO(now))}`],
[{ endDate: now }, `endDate=${encodeURIComponent(formatISO(now))}`],
[ [
{ startDate: now, endDate: now }, { startDate: now, endDate: now } as DateRange,
`startDate=${encodeURIComponent(formatISO(now))}&endDate=${encodeURIComponent(formatISO(now))}`, `startDate=${encodeURIComponent(formatISO(startOfDay(now)))}&endDate=${encodeURIComponent(formatISO(endOfDay(now)))}`,
], ],
])('redirects to first page when date range changes', (dates, expectedQuery) => { ])('redirects to first page when date range changes', async (dates, expectedQuery) => {
const wrapper = createWrapper(); const { user } = setUp();
const dateRange = wrapper.find(DateRangeSelector);
await user.click(screen.getByRole('button', { name: 'All short URLs' }));
expect(await screen.findByRole('menu')).toBeInTheDocument();
expect(navigate).not.toHaveBeenCalled(); expect(navigate).not.toHaveBeenCalled();
dateRange.simulate('datesChange', dates); dates.startDate && await user.type(screen.getByPlaceholderText('Since...'), formatDate()(dates.startDate) ?? '');
expect(navigate).toHaveBeenCalledWith(`/server/1/list-short-urls/1?${expectedQuery}`); dates.endDate && await user.type(screen.getByPlaceholderText('Until...'), formatDate()(dates.endDate) ?? '');
expect(navigate).toHaveBeenLastCalledWith(`/server/1/list-short-urls/1?${expectedQuery}`);
}); });
it.each([ it.each([
['tags=foo,bar,baz', Mock.of<ReachableServer>({ version: '3.0.0' }), 1], ['tags=foo,bar,baz', Mock.of<ReachableServer>({ version: '3.0.0' }), true],
['tags=foo,bar', Mock.of<ReachableServer>({ version: '3.1.0' }), 1], ['tags=foo,bar', Mock.of<ReachableServer>({ version: '3.1.0' }), true],
['tags=foo', Mock.of<ReachableServer>({ version: '3.0.0' }), 0], ['tags=foo', Mock.of<ReachableServer>({ version: '3.0.0' }), false],
['', Mock.of<ReachableServer>({ version: '3.0.0' }), 0], ['', Mock.of<ReachableServer>({ version: '3.0.0' }), false],
['tags=foo,bar,baz', Mock.of<ReachableServer>({ version: '2.10.0' }), 0], ['tags=foo,bar,baz', Mock.of<ReachableServer>({ version: '2.10.0' }), false],
['', Mock.of<ReachableServer>({ version: '2.10.0' }), 0], ['', Mock.of<ReachableServer>({ version: '2.10.0' }), false],
])( ])(
'renders tags mode toggle if the server supports it and there is more than one tag selected', 'renders tags mode toggle if the server supports it and there is more than one tag selected',
(search, selectedServer, expectedTagToggleComponents) => { (search, selectedServer, shouldHaveComponent) => {
const wrapper = createWrapper(search, selectedServer); setUp(search, selectedServer);
const toggle = wrapper.find(TooltipToggleSwitch);
expect(toggle).toHaveLength(expectedTagToggleComponents); if (shouldHaveComponent) {
expect(screen.getByLabelText('Change tags mode')).toBeInTheDocument();
} else {
expect(screen.queryByLabelText('Change tags mode')).not.toBeInTheDocument();
}
}, },
); );
it.each([ it.each([
['', 'Short URLs including any tag.', false], ['', 'With any of the tags.'],
['&tagsMode=all', 'Short URLs including all tags.', true], ['&tagsMode=all', 'With all the tags.'],
['&tagsMode=any', 'Short URLs including any tag.', false], ['&tagsMode=any', 'With any of the tags.'],
])('expected tags mode tooltip title', (initialTagsMode, expectedToggleText, expectedChecked) => { ])('expected tags mode tooltip title', async (initialTagsMode, expectedToggleText) => {
const wrapper = createWrapper(`tags=foo,bar${initialTagsMode}`, Mock.of<ReachableServer>({ version: '3.0.0' })); const { user } = setUp(`tags=foo,bar${initialTagsMode}`, Mock.of<ReachableServer>({ version: '3.0.0' }));
const toggle = wrapper.find(TooltipToggleSwitch);
expect(toggle.prop('children')).toEqual(expectedToggleText); await user.hover(screen.getByLabelText('Change tags mode'));
expect(toggle.prop('checked')).toEqual(expectedChecked); expect(await screen.findByRole('tooltip')).toHaveTextContent(expectedToggleText);
}); });
it.each([ it.each([
['', 'tagsMode=all'], ['', 'tagsMode=all'],
['&tagsMode=all', 'tagsMode=any'], ['&tagsMode=all', 'tagsMode=any'],
['&tagsMode=any', 'tagsMode=all'], ['&tagsMode=any', 'tagsMode=all'],
])('redirects to first page when tags mode changes', (initialTagsMode, expectedRedirectTagsMode) => { ])('redirects to first page when tags mode changes', async (initialTagsMode, expectedRedirectTagsMode) => {
const wrapper = createWrapper(`tags=foo,bar${initialTagsMode}`, Mock.of<ReachableServer>({ version: '3.0.0' })); const { user } = setUp(`tags=foo,bar${initialTagsMode}`, Mock.of<ReachableServer>({ version: '3.0.0' }));
const toggle = wrapper.find(TooltipToggleSwitch);
expect(navigate).not.toHaveBeenCalled(); expect(navigate).not.toHaveBeenCalled();
toggle.simulate('change'); await user.click(screen.getByLabelText('Change tags mode'));
expect(navigate).toHaveBeenCalledWith(expect.stringContaining(expectedRedirectTagsMode)); expect(navigate).toHaveBeenCalledWith(expect.stringContaining(expectedRedirectTagsMode));
}); });
it('handles order through dropdown', () => { it('handles order through dropdown', async () => {
const wrapper = createWrapper(); const { user } = setUp();
const clickMenuItem = async (name: string | RegExp) => {
await user.click(screen.getByRole('button', { name: 'Order by...' }));
await user.click(await screen.findByRole('menuitem', { name }));
};
expect(wrapper.find(OrderingDropdown).prop('order')).toEqual({}); await clickMenuItem(/^Short URL/);
expect(handleOrderBy).toHaveBeenCalledWith('shortCode', 'ASC');
wrapper.find(OrderingDropdown).simulate('change', 'visits', 'ASC'); await clickMenuItem(/^Title/);
expect(handleOrderBy).toHaveBeenCalledWith('visits', 'ASC'); expect(handleOrderBy).toHaveBeenCalledWith('title', 'ASC');
wrapper.find(OrderingDropdown).simulate('change', 'shortCode', 'DESC'); await clickMenuItem(/^Long URL/);
expect(handleOrderBy).toHaveBeenCalledWith('shortCode', 'DESC'); expect(handleOrderBy).toHaveBeenCalledWith('longUrl', 'ASC');
wrapper.find(OrderingDropdown).simulate('change', undefined, undefined);
expect(handleOrderBy).toHaveBeenCalledWith(undefined, undefined);
}); });
}); });