From ed038b97997b79c913ca3daee3185f8783eafc38 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Mon, 8 Nov 2021 23:41:17 +0100 Subject: [PATCH] Fixed ShortUrlsList test --- src/short-urls/ShortUrlsList.tsx | 43 +++++++++++----------- src/short-urls/ShortUrlsTable.tsx | 4 ++- test/short-urls/ShortUrlsList.test.tsx | 50 +++++++++++++------------- 3 files changed, 50 insertions(+), 47 deletions(-) diff --git a/src/short-urls/ShortUrlsList.tsx b/src/short-urls/ShortUrlsList.tsx index 7c8fcda4..1a303cbd 100644 --- a/src/short-urls/ShortUrlsList.tsx +++ b/src/short-urls/ShortUrlsList.tsx @@ -1,25 +1,20 @@ -import { head, keys, values } from 'ramda'; -import { FC, useEffect, useState } from 'react'; +import { head, keys, pipe, values } from 'ramda'; +import { FC, useEffect, useMemo, useState } from 'react'; import { RouteComponentProps } from 'react-router'; import { Card } from 'reactstrap'; import SortingDropdown from '../utils/SortingDropdown'; import { determineOrderDir, Order, OrderDir } from '../utils/helpers/ordering'; import { getServerId, SelectedServer } from '../servers/data'; import { boundToMercureHub } from '../mercure/helpers/boundToMercureHub'; -import { parseQuery } from '../utils/helpers/query'; import { Topics } from '../mercure/helpers/Topics'; import { TableOrderIcon } from '../utils/table/TableOrderIcon'; import { ShortUrlsList as ShortUrlsListState } from './reducers/shortUrlsList'; import { OrderableFields, ShortUrlsListParams, SORTABLE_FIELDS } from './reducers/shortUrlsListParams'; import { ShortUrlsTableProps } from './ShortUrlsTable'; import Paginator from './Paginator'; +import { ShortUrlListRouteParams, useShortUrlsQuery } from './helpers/hooks'; -interface RouteParams { - page: string; - serverId: string; -} - -export interface ShortUrlsListProps extends RouteComponentProps { +interface ShortUrlsListProps extends RouteComponentProps { selectedServer: SelectedServer; shortUrlsList: ShortUrlsListState; listShortUrls: (params: ShortUrlsListParams) => void; @@ -29,21 +24,26 @@ export interface ShortUrlsListProps extends RouteComponentProps { type ShortUrlsOrder = Order; -const ShortUrlsList = (ShortUrlsTable: FC) => boundToMercureHub(({ +const ShortUrlsList = (ShortUrlsTable: FC, SearchBar: FC) => boundToMercureHub(({ listShortUrls, resetShortUrlParams, shortUrlsListParams, match, location, + history, shortUrlsList, selectedServer, }: ShortUrlsListProps) => { + const serverId = getServerId(selectedServer); const { orderBy } = shortUrlsListParams; const [ order, setOrder ] = useState({ field: orderBy && (head(keys(orderBy)) as OrderableFields), dir: orderBy && head(values(orderBy)), }); + const [{ tags, search }, toFirstPage ] = useShortUrlsQuery({ history, match, location }); + const decodedTags = useMemo(() => tags?.split(',').map(decodeURIComponent) ?? [], [ tags ]); const { pagination } = shortUrlsList?.shortUrls ?? {}; + const refreshList = (extraParams: ShortUrlsListParams) => listShortUrls({ ...shortUrlsListParams, ...extraParams }); const handleOrderBy = (field?: OrderableFields, dir?: OrderDir) => { setOrder({ field, dir }); @@ -52,30 +52,31 @@ const ShortUrlsList = (ShortUrlsTable: FC) => boundToMercur const orderByColumn = (field: OrderableFields) => () => handleOrderBy(field, determineOrderDir(field, order.field, order.dir)); const renderOrderIcon = (field: OrderableFields) => ; + const addTag = pipe( + (newTag: string) => [ ...new Set([ ...decodedTags, newTag ]) ].join(','), + (tags) => toFirstPage({ tags }), + ); + useEffect(() => resetShortUrlParams, []); useEffect(() => { - const { tag } = parseQuery<{ tag?: string }>(location.search); - const tags = tag ? [ decodeURIComponent(tag) ] : shortUrlsListParams.tags; - - refreshList({ page: match.params.page, tags, itemsPerPage: undefined }); - - return resetShortUrlParams; - }, []); + refreshList({ page: match.params.page, searchTerm: search, tags: decodedTags, itemsPerPage: undefined }); + }, [ match.params.page, search, decodedTags ]); return ( <> +
refreshList({ tags: [ ...shortUrlsListParams.tags ?? [], tag ] })} + orderByColumn={orderByColumn} + renderOrderIcon={renderOrderIcon} + onTagClick={addTag} /> - + ); diff --git a/src/short-urls/ShortUrlsTable.tsx b/src/short-urls/ShortUrlsTable.tsx index 5a51db85..ae73ad9e 100644 --- a/src/short-urls/ShortUrlsTable.tsx +++ b/src/short-urls/ShortUrlsTable.tsx @@ -35,7 +35,9 @@ export const ShortUrlsTable = (ShortUrlsRow: FC) => ({ if (error) { return ( - Something went wrong while loading short URLs :( + + Something went wrong while loading short URLs :( + ); } diff --git a/test/short-urls/ShortUrlsList.test.tsx b/test/short-urls/ShortUrlsList.test.tsx index 3524e3b7..ebb09941 100644 --- a/test/short-urls/ShortUrlsList.test.tsx +++ b/test/short-urls/ShortUrlsList.test.tsx @@ -1,19 +1,24 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { ReactElement } from 'react'; import { Mock } from 'ts-mockery'; -import shortUrlsListCreator, { ShortUrlsListProps } from '../../src/short-urls/ShortUrlsList'; +import { History, Location } from 'history'; +import { match } from 'react-router'; +import shortUrlsListCreator from '../../src/short-urls/ShortUrlsList'; import { ShortUrl } from '../../src/short-urls/data'; import { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub'; import { ShortUrlsList as ShortUrlsListModel } from '../../src/short-urls/reducers/shortUrlsList'; import SortingDropdown from '../../src/utils/SortingDropdown'; import { OrderableFields, OrderBy } from '../../src/short-urls/reducers/shortUrlsListParams'; import Paginator from '../../src/short-urls/Paginator'; +import { ReachableServer } from '../../src/servers/data'; +import { ShortUrlListRouteParams } from '../../src/short-urls/helpers/hooks'; describe('', () => { let wrapper: ShallowWrapper; const ShortUrlsTable = () => null; + const SearchBar = () => null; const listShortUrlsMock = jest.fn(); - const resetShortUrlParamsMock = jest.fn(); + const push = jest.fn(); const shortUrlsList = Mock.of({ shortUrls: { data: [ @@ -26,22 +31,18 @@ describe('', () => { ], }, }); - const ShortUrlsList = shortUrlsListCreator(ShortUrlsTable); + const ShortUrlsList = shortUrlsListCreator(ShortUrlsTable, SearchBar); const createWrapper = (orderBy: OrderBy = {}) => shallow( ()} {...Mock.of({ mercureInfo: { loading: true } })} listShortUrls={listShortUrlsMock} - resetShortUrlParams={resetShortUrlParamsMock} - shortUrlsListParams={{ - page: '1', - tags: [ 'test tag' ], - searchTerm: 'example.com', - orderBy, - }} - match={{ params: {} } as any} - location={{} as any} + resetShortUrlParams={jest.fn()} + shortUrlsListParams={{ page: '1', orderBy }} + match={Mock.of>({ params: {} })} + location={Mock.of({ search: '?tags=test%20tag&search=example.com' })} shortUrlsList={shortUrlsList} + history={Mock.of({ push })} + selectedServer={Mock.of({ id: '1' })} />, ).dive(); // Dive is needed as this component is wrapped in a HOC @@ -56,6 +57,11 @@ describe('', () => { expect(wrapper.find(ShortUrlsTable)).toHaveLength(1); expect(wrapper.find(SortingDropdown)).toHaveLength(1); expect(wrapper.find(Paginator)).toHaveLength(1); + expect(wrapper.find(SearchBar)).toHaveLength(1); + }); + + it('passes current query to paginator', () => { + expect(wrapper.find(Paginator).prop('currentQueryString')).toEqual('?tags=test%20tag&search=example.com'); }); it('gets list refreshed every time a tag is clicked', () => { @@ -63,16 +69,10 @@ describe('', () => { wrapper.find(ShortUrlsTable).simulate('tagClick', 'bar'); wrapper.find(ShortUrlsTable).simulate('tagClick', 'baz'); - expect(listShortUrlsMock).toHaveBeenCalledTimes(3); - expect(listShortUrlsMock).toHaveBeenNthCalledWith(1, expect.objectContaining({ - tags: [ 'test tag', 'foo' ], - })); - expect(listShortUrlsMock).toHaveBeenNthCalledWith(2, expect.objectContaining({ - tags: [ 'test tag', 'bar' ], - })); - expect(listShortUrlsMock).toHaveBeenNthCalledWith(3, expect.objectContaining({ - tags: [ 'test tag', 'baz' ], - })); + expect(push).toHaveBeenCalledTimes(3); + expect(push).toHaveBeenNthCalledWith(1, expect.stringContaining(`tags=${encodeURIComponent('test tag,foo')}`)); + expect(push).toHaveBeenNthCalledWith(2, expect.stringContaining(`tags=${encodeURIComponent('test tag,bar')}`)); + expect(push).toHaveBeenNthCalledWith(3, expect.stringContaining(`tags=${encodeURIComponent('test tag,baz')}`)); }); it('invokes order icon rendering', () => { @@ -88,7 +88,7 @@ describe('', () => { expect(renderIcon('visits').props.currentOrder).toEqual({ field: 'visits', dir: 'ASC' }); }); - it('handles order by through table', () => { + it('handles order through table', () => { const orderByColumn: (field: OrderableFields) => Function = wrapper.find(ShortUrlsTable).prop('orderByColumn'); orderByColumn('visits')(); @@ -107,7 +107,7 @@ describe('', () => { })); }); - it('handles order by through dropdown', () => { + it('handles order through dropdown', () => { expect(wrapper.find(SortingDropdown).prop('order')).toEqual({}); wrapper.find(SortingDropdown).simulate('change', 'visits', 'ASC');