1

I'm trying to test a react component where a fetch call occurs. The component:

class SearchResults extends React.Component<{ tag: string; setSpinner: (value: boolean) => void }> {
  state: searchResultsState = {
    results: [],
    tag: '',
    cardFull: null,
  };
  constructor(props: { tag: string; setSpinner: (value: boolean) => void }) {
    super(props);
    this.handleCardModal = this.handleCardModal.bind(this);
  }
  componentDidMount() {
    getResponse(
      this.props.tag,
      (res) => {
        this.setState({ results: res, tag: this.props.tag });
      },
      this.props.setSpinner
    );
  }
  componentDidUpdate(prevProps: { tag: string }) {
    if (this.props.tag !== prevProps.tag) {
      getResponse(
        this.props.tag,
        (res) => this.setState({ results: res, tag: this.props.tag }),
        this.props.setSpinner
      );
    }
  }
  handleCardModal() {
    this.setState({ cardFull: null });
  }
  render() {
    return (
      <Fragment>
        {this.state.cardFull && (
          <CardFull data={this.state.cardFull} handleClick={this.handleCardModal} />
        )}
        {this.state.cardFull && <div className="blackout" onClick={this.handleCardModal} />}
        <div className="search-results">
          {this.state.results.length === 0 && this.state.tag !== '' && <div>Sorry, no matched</div>}
          {this.state.results.map((result) => (
            <CardUI
              key={result.id}
              className="card"
              variant="outlined"
              sx={{ minWidth: 275 }}
              onClick={() => {
                const currentCard = this.state.results.filter((res) => res.id === result.id)[0];
                this.setState({ ...this.state, cardFull: currentCard });
              }}
            >
              <CardMedia component="img" height="194" image={getUrl(result)} alt={result.title} />
              <CardContent>
                <p>{result.title}</p>
              </CardContent>
            </CardUI>
          ))}
        </div>
      </Fragment>
    );
  }
}

First I tried to use jest-fetch-mock.

import '@testing-library/jest-dom';
import { render } from '@testing-library/react';
import renderer from 'react-test-renderer';
import SearchResults from '../../src/components/SearchResults/SearchResults';
import sampleSearchResults from '../__fixtures__/sampleSearchResults';
import fetch from 'jest-fetch-mock';

fetch.enableMocks();
beforeEach(() => {
  fetch.resetMocks();
});
const setSpinner = jest.fn();

describe('Search Results component', () => {
  fetch.mockResponseOnce(JSON.stringify({ photos: sampleSearchResults }));
  test('Search Results matches snapshot', () => {
    const searchResults = renderer
      .create(<SearchResults tag={''} setSpinner={setSpinner} />)
      .toJSON();
    expect(searchResults).toMatchSnapshot();
  });
  test('search results renders correctly', () => {
    render(<SearchResults setSpinner={setSpinner} tag={'dove'} />);
  });
});

But it gives the error during tests:

  console.error
    FetchError {
      message: 'invalid json response body at  reason: Unexpected end of JSON input',
      type: 'invalid-json'
    }

So, I've decided to mock fetch manually

import React from 'react';
import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/react';
import renderer from 'react-test-renderer';
import SearchResults from '../../src/components/SearchResults/SearchResults';
import sampleSearchResults from '../__fixtures__/sampleSearchResults';

const setSpinner = jest.fn();
global.fetch = jest.fn(() =>
  Promise.resolve({
    json: () => Promise.resolve({ photos: sampleSearchResults }),
  })
) as jest.Mock;

describe('Search Results component', () => {
  test('Search Results matches snapshot', () => {
    const searchResults = renderer
      .create(<SearchResults tag={''} setSpinner={setSpinner} />)
      .toJSON();
    expect(searchResults).toMatchSnapshot();
  });
  test('search results renders correctly', () => {
    render(<SearchResults setSpinner={setSpinner} tag={'dove'} />);
    const title = screen.getByText(/Eurasian Collared Dove (Streptopelia decaocto)/i);
    expect(title).toBeInTheDocument(); //mistake
  });
});

Now fetch mock works correct, but it renders only one div - search results and doesn't render card. How can I test my component? Thank you.

Oksana
  • 25
  • 5

0 Answers0