'How can i test add filters button when clicked it opens search filters popup but before the button appears there is loading spinner?
add filter button is appeared when loading spinner is completed(means there is dispatch trigger action called loadListings which set loading to true).
I'm using react testing library, how can i achieve this by using mockedAxios? or any idea.
useEffect(() => {
dispatch(loadListings());
}, [dispatch]);
Search.js
import React, {useEffect} from 'react';
import {useDispatch, useSelector} from 'react-redux';
import {useNavigate} from '@reach/router';
import Picture from '@/components/picture';
import Button from '@/components/button';
import Pagination from '@/components/pagination';
import Tag from '@/components/tag';
import Spinner from '@/components/basic-spinner';
import Footer from '@/components/footer/footer';
import {
getAppliedSelectFilters,
getListings,
getListingsTotal,
listingsLoading,
} from '@/selectors/search';
import {showPopup} from '@/redux/modules/app/actions';
import {
saveDraftFilters,
clearDraftFilters,
draftSelectFilter,
clearAppliedFilter,
clearAllAppliedFilters,
loadListings,
} from '@/redux/modules/search/actions';
import {getSelectFilters, getSelectFiltersGroups} from '@/selectors/search';
import SearchFilterPopup from '@/components/search-filter-popup';
const Search = () => {
const navigate = useNavigate();
const dispatch = useDispatch();
const appliedFilters = useSelector(getAppliedSelectFilters);
const hits = useSelector(getListings);
const hitsCount = useSelector(getListingsTotal);
const loading = useSelector(listingsLoading);
const hitsPerPage = 10;
useEffect(() => {
dispatch(loadListings());
}, [dispatch]);
const showFilters = () => {
dispatch(
showPopup({
content: (
<SearchFilterPopup
saveDraftFilters={saveDraftFilters}
clearDraftFilters={clearDraftFilters}
draftSelectFilter={draftSelectFilter}
loadListings={loadListings}
selectFiltersSelector={getSelectFilters}
selectFiltersGroupsSelector={getSelectFiltersGroups}
/>
),
showClose: false,
}),
);
};
const handleTagClicked = item => {
dispatch(clearAppliedFilter(item));
dispatch(loadListings());
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const handleClearAllFilters = () => {
dispatch(clearAllAppliedFilters());
dispatch(loadListings());
};
const handleBusinessSelected = ({identity, urlName}) => () => {
navigate(`/listings/${urlName}/${identity}`);
};
const handlePaginationPageClick = data => {
const indexOfLastHit = (data.selected + 1) * hitsPerPage;
const pages = indexOfLastHit - hitsPerPage;
dispatch(loadListings(pages));
};
return (
<section className='np-flex np-w-full np-filter-container np-overflow-x-hidden np-max-w-1300px np-flex-col np-justify-between np-mx-auto np-min-h-screen'>
<div className='np-flex np-pd np-flex-col np-justify-center'>
{!loading ? (
<div className='np-w-full'>
<div className='np-w-full'>
<h3 className='np-font-primary np-result-places np-mb-3 np-pt-8 np-truncate'>
<span>{hitsCount === 0 ? hitsCount : `${hitsCount}+`}</span>{' '}
{`Place${hitsCount !== 1 ? 's' : ''}`} in Dar es Salaam
</h3>
<div className='np-mb-3 np-flex'>
<Button
variant='link'
className='np-font-semibold np-uppercase'
onClick={showFilters}
>
<span>Add Filters</span>
</Button>
</div>
<div className='np-overflow-x-auto np-flex np-w-full'>
{appliedFilters.map(item => (
<Tag
key={item.name}
item={item}
labelKey='label'
toBe='removed'
onClick={handleTagClicked}
className='np-Tag-width'
/>
))}
{appliedFilters.length ? (
<Button variant='link' onClick={handleClearAllFilters}>
Clear all filters
</Button>
) : null}
</div>
</div>
</div>
) : null}
<div className='np-flex np-flex-wrap np-justify-center'>
{loading ? (
<div className='np-pt-32 np-h-80vh np-w-full np-flex np-justify-center'>
<Spinner size='large' label='loading' color='primary' />
</div>
) : hitsCount > 0 ? (
hits.map(hit => (
<div
key={hit.id}
className='np-search-card'
onClick={handleBusinessSelected(hit)}
>
<Picture
height='2/3'
src={`${
process.env.IMAGE_SERVICE_URL
}/cover:entropy/340x226/${hit.photos.length > 0 &&
hit.photos[hit.photos.length - 1].name}`}
className='np-overflow-hidden np-rounded-sm np-cursor-pointer'
/>
<section className='np-py-2 np-leading-normal'>
<h4 className='np-truncate'>{hit.category}</h4>
<h3 className='np-font-primary np-font-medium np-cursor-pointer np-truncate np-capitalize'>
{hit.name}{' '}
</h3>
<h4 className='np-text np-text-gray np-truncate'>
<span className='icon location'></span> {hit.location}
</h4>
</section>
</div>
))
) : null}
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
actions.js
export const clearDraftFilters = () => ({
type: types.CLEAR_DRAFT_FILTERS,
});
export const loadListings = payload => ({
type: types.LOAD_LISTINGS,
payload,
});
export const loadListingStart = () => ({
type: types.LOAD_LISTINGS_START,
});
export const loadListingSuccess = payload => ({
type: types.LOAD_LISTINGS_SUCCESS,
payload,
});
export const loadListingError = error => ({
type: types.LOAD_LISTINGS_ERROR,
payload: error,
error: true,
});
epics.js
export const loadListingsEpic = (action$, state$, {API}) =>
action$.pipe(
filter(action => action.type === types.LOAD_LISTINGS),
switchMap(action => {
const features = getAppliedPlaceFeatureFilters(state$.value).join(',');
const category = getAppliedPlaceCategoryFilters(state$.value).join(',');
const q = get(state$.value, 'search.q', '');
const page = action.payload ? action.payload : null;
const query = omitEmpty({q, category, features, page, limit: 12});
return defer(() => API.getListings(query)).pipe(
switchMap(response => {
const [error, result] = response;
if (error) {
return of(actions.loadListingError(error));
}
return of(actions.loadListingSuccess(result));
}),
startWith(actions.loadListingStart()),
);
}),
);
reducers.js
const initialState = {
selectFilters: [],
suggestions: {docs: [], total: 0, loading: false},
};
export default (state = initialState, action) => {
switch (action.type) {
case types.LOAD_LISTINGS_START:
return {
...state,
listings: {loading: true},
};
case types.LOAD_LISTINGS_SUCCESS:
return {
...state,
listings: {loading: false, ...action.payload},
};
case types.LOAD_LISTINGS_ERROR:
return {
...state,
listings: {loading: false, error: action.payload},
};
default:
return state;
}
};
Search.test.js
import React from 'react';
import {render, screen, fireEvent} from '@/utils/testUtils';
import {waitFor} from '@testing-library/react';
import mockedAxios from 'axios';
import Search from './Search';
import { async } from 'rxjs/internal/scheduler/async';
jest.mock('axios');
describe('Search', () => {
afterEach(() => {
jest.resetAllMocks();
});
test('should render search result filters popup when add filters button is clicked', async () =>{
render(<Search />);
await waitFor(() => fireEvent.click(screen.getByRole('button', {name: 'Add Filters'}))
)
})
});
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
