Patrick Passarella

A React image component with a fallback url

And how to test it with Jest and React Testing Library

Patrick Passarella - 5th May, 2020

Photo by NeONBRAND on Unsplash

I am sure that you already found a website with an image not loading, maybe you don't really cared that much, but a lot of users care and find it very irritating. It's something incredible simple to solve, that adds a solid value to your application/website.

What we will build, is a image component that can handle broken images, by having a fallback URL. To start off, let's build a simple Image React component with Typescript.

1interface Props {
2 url: string;
3};
4
5const Image: React.FC<Props> = ({url, ...rest}) => (
6 <img src={url} {...rest} />
7);

As you can see, it's just a component that returns a img tag, now to add the fallback url we can use the property onError that the img tag has and almost no one knows about it.

1interface Props {
2 url: string;
3 fallback?: string;
4};
5
6const handleImageError = fallback => event => event.target.src = fallback;
7
8const Image: React.FC<Props> = ({url, fallback, ...rest}) => (
9 <img src={url} onError={handleImageError(fallback)} {...rest} />
10);

So if the main url fails, it will change the src from the image to the passed fallback url, which could be for example a default image. And it works! you can already try it, check the gif below on how to manually test it.

gif

Just change the URL to something non-existent and you will see that it will change the image source to the fallback one.
Now, how to test it with Jest? After long and painful hours of trying to test it properly, I found (with the help of the community), a not-too-bad solution, it's not the best, but at least we can test it properly.

1import React from 'react';
2import '@testing-library/jest-dom/extend-expect';
3import { render, fireEvent, screen } from '@testing-library/react';
4import Image from '..';
5
6const imageUrl = 'https://images.unsplash.com/photo-1531436107035-40b2e85b7a1b?ixlib=rb-1.2.1&amp;ixid=eyJhcHBfaWQiOjEyMDd9&amp;w=1000&amp;q=80';
7const fallbackUrl = 'https://images.unsplash.com/photo-1549880338-65ddcdfd017b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=750&q=80';
8const imageErrorUrl = 'error://test';
9
10describe('Image', () => {
11 it('should render the fallback image if the main one has an error', () => {
12 render(
13 <Image
14 src={imageErrorUrl}
15 fallback={fallbackUrl}
16 />
17 );
18
19 const image = screen.getByRole("img");
20 expect(image).toHaveAttribute('src', imageErrorUrl);
21 fireEvent.error(image); // Here we trigger the error event
22 expect(image).toHaveAttribute('src', fallbackUrl); // and check if the src changed
23 });
24});

Remembering we need the @testing-library/jest-dom/extend-expect to be able to use the toHaveAttribute property. It's a simple test, we are getting the image tag, forcing an error event to happen there, and checking if the image src has changed to the fallbackUrl.

That's it! now you have a simple image component with a fallback, that you can reuse it in your application.

Thanks for reading!Enjoyed the content or just want to send me a message? Follow me on Twitter!
Patrick Passarella (@P_Passarella) | Twitter
Twitter user