Ad

How To Test FileReader Onload Using Simulate Change In Jest?

- 1 answer

SimpleDialog.jsx

const [imagePreview, setImagePreview] = React.useState(null);

const handleChangeImage = event => {
    let reader = new FileReader();
    let file = event.target.files[0];

    reader.onload = event => {
        console.log(event);

        setImagePreview(event.target.result);
    };

    reader.readAsDataURL(file);
};

return (
    <div>
        <input
            accept="image/*"
            id="contained-button-file"
            multiple
            type="file"
            style={{ display: 'none' }}
            onChange={handleChangeImage}
        />

        <img id="preview" src={imagePreview} />
    </div>
);

SimpleDialog.test.js

it('should change image src', () => {
    const event = {
        target: {
            files: [
                {
                    name: 'image.png',
                    size: 50000,
                    type: 'image/png'
                }
            ]
        }
    };

    let spy = jest
        .spyOn(FileReader.prototype, 'onload')
        .mockImplementation(() => null);

    wrapper.find('input[type="file"]').simulate('change', event);

    expect(spy).toHaveBeenCalled();

    expect(wrapper.find('#preview').prop('src')).not.toBeNull();
});

When running the test it gives me the error TypeError: Illegal invocation.

Anyone who can help me with this unit test? I Just want to simulate on change if the src of an image has value or not.

Ad

Answer

The cause of the error is that onload is defined as property descriptor and assigning it to FileReader.prototype which is done by spyOn isn't supported.

There's no reason to mock onload because it's assigned in tested code and needs to be tested.

The straightforward way is to not patch JSDOM FileReader implementation but stub it entirely:

jest.spyOn(global, 'FileReader').mockImplementation(function () {
    this.readAsDataURL = jest.fn();
});

wrapper.find('input[type="file"]').simulate('change', event);


let reader = FileReader.mock.instances[0];
expect(reader.readAsDataURL).toHaveBeenCalledWith(...);
expect(reader.onload).toBe(expect.any(Function));

expect(wrapper.find('#preview').prop('src')).toBeNull();

reader.onload({ target: { result: 'foo' } });

expect(wrapper.find('#preview').prop('src')).toBe('foo');
Ad
source: stackoverflow.com
Ad