I have the following react code in my project
import React from 'react';
import { Upload } from 'antd';
const { Dragger } = Upload;
...
<Dragger
accept={ACCEPTED_FORMATS}
beforeUpload={beforeUpload}
data-testid="upload-dragger"
maxCount={1}
onChange={({ file: { status } }) => {
if (status === 'done') onUploadComplete();
}}
progress={progress}
showUploadList={false}
>
{/* here i have a button, code ommited for clarity, if needed i'll post it */}
</Dragger>
And I want to test if the callback function onUploadComplete()
was called when file.status is 'done'
.
Here is how i am doing the test right now:
import React from 'react';
import { fireEvent, render, waitFor } from '@testing-library/react';
import { act } from 'react-dom/test-utils';
import { DraggerProps } from 'antd/lib/upload';
import userEvent from '@testing-library/user-event';
import UploadCompanyLogo from '../UploadCompanyLogo'; // This is the component where the dragger is placed
jest.mock('antd', () => {
const antd = jest.requireActual('antd');
const { Upload } = antd;
const { Dragger } = Upload;
const MockedDragger = (props: DraggerProps) => {
console.log('log test');
return (
<Dragger
{...props}
action="greetings"
customRequest={({ onSuccess }) => {
setTimeout(() => {
onSuccess('ok');
}, 0);
}}
/>
);
};
return { ...antd, Upload: { ...Upload, Dragger: MockedDragger } };
});
antd
will be imported), simulate an upload and then checks if the callback has been called.it('completes the image upload', async () => {
const flushPromises = () => new Promise(setImmediate);
const { getByTestId } = render(elementRenderer({ onUploadComplete }));
const file = new File(['(⌐□_□)'], 'chucknorris.png', { type: 'image/png' });
const uploadDragger = await waitFor(() => getByTestId('upload-dragger'));
await act(async () => {
userEvent.upload(uploadDragger, file);
});
await flushPromises();
expect(onUploadComplete).toHaveBeenCalledTimes(1);
expect(onUploadComplete).toHaveBeenCalledWith();
});
const elementRenderer = ({
onUploadComplete = () => {}
}) => (
<ApplicationProvider>
<UploadCompanyLogo
onUploadComplete={onUploadComplete}
/>
</ApplicationProvider>
);
import React, { PropsWithChildren } from 'react';
import { ConfigProvider as AntdProvider } from 'antd';
import { RendererProvider as FelaProvider } from 'react-fela';
import { createRenderer } from 'fela';
import { I18nextProvider } from 'react-i18next';
import antdExternalContainer, {
EXTERNAL_CONTAINER_ID,
} from 'src/util/antdExternalContainer';
import { antdLocale } from 'src/util/locales';
import rendererConfig from 'src/fela/felaConfig';
import i18n from 'src/i18n';
const ApplicationProvider = (props: PropsWithChildren<{}>) => {
const { children } = props;
return (
<AntdProvider locale={antdLocale} getPopupContainer={antdExternalContainer}>
<FelaProvider renderer={createRenderer(rendererConfig)}>
<I18nextProvider i18n={i18n}>
<div className="antd-local">
<div id={EXTERNAL_CONTAINER_ID} />
{children}
</div>
</I18nextProvider>
</FelaProvider>
</AntdProvider>
);
};
export default ApplicationProvider;
This is currently not working, but have already been improved with the help of @diedu.
The console.log()
I have put in the MockedDragger it's currently not showing. If I put a console.log()
in both component and mockedDragger, it prints the component log.
Any tips on how to proceed? I have already seen this issue and didn’t help.
The first thing you should change is the return you are doing in your mock. You are returning a new component called MockedDragger
, not Dragger
.
return { ...antd, Upload: { ...Upload, Dragger: MockedDragger } };
The next thing is the event firing. According to this issue you should use RTL user-event library and wrap the call in act
import userEvent from "@testing-library/user-event";
...
await act(async () => {
userEvent.upload(uploadDragger, file);
});
...
and finally, due to some asynchronous code running you need to flush the pending promises before checking the call
const flushPromises = () => new Promise(setImmediate);
...
await flushPromises();
expect(onUploadComplete).toHaveBeenCalled()
this is the full version
import { render, waitFor } from "@testing-library/react";
import App from "./App";
import userEvent from "@testing-library/user-event";
import { act } from "react-dom/test-utils";
import { DraggerProps } from "antd/lib/upload";
jest.mock('antd', () => {
const antd = jest.requireActual('antd');
const { Upload } = antd;
const { Dragger } = Upload;
const MockedDragger = (props: DraggerProps) => {
return <Dragger {...props} customRequest={({ onSuccess }) => {
setTimeout(() => {
onSuccess('ok');
}, 0)
}} />;
};
return { ...antd, Upload: { ...Upload, Dragger: MockedDragger } };
});
it("completes the image upload", async () => {
const onUploadComplete = jest.fn();
const flushPromises = () => new Promise(setImmediate);
const { getByTestId } = render(
<App onUploadComplete={onUploadComplete} />
);
const file = new File(["(⌐□_□)"], "chucknorris.png", { type: "image/png" });
const uploadDragger = await waitFor(() => getByTestId("upload-dragger"));
await act(async () => {
userEvent.upload(uploadDragger, file);
});
await flushPromises();
expect(onUploadComplete).toHaveBeenCalled();
});
Unfortunately, I couldn't set up a codesandbox to show it's working, but let me know if you face any issue and I can push the code to a repo.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With