'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