admin管理员组

文章数量:1025457

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:

  1. I have a jest.mock to simulate a dumb request that will always succeed
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 ponent 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 } };
});
  1. The test itself (same file as the mock), where it renders the ponent (where antd will be imported), simulate an upload and then checks if the callback has been called.
it('pletes 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();
  });
  1. elementRenderer
const elementRenderer = ({
  onUploadComplete = () => {}
}) => (
  <ApplicationProvider>
    <UploadCompanyLogo
      onUploadComplete={onUploadComplete}
    />
  </ApplicationProvider>
);
  1. 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 ponent and mockedDragger, it prints the ponent log.

Any tips on how to proceed? I have already seen this issue and didn’t help.

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:

  1. I have a jest.mock to simulate a dumb request that will always succeed
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 ponent 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 } };
});
  1. The test itself (same file as the mock), where it renders the ponent (where antd will be imported), simulate an upload and then checks if the callback has been called.
it('pletes 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();
  });
  1. elementRenderer
const elementRenderer = ({
  onUploadComplete = () => {}
}) => (
  <ApplicationProvider>
    <UploadCompanyLogo
      onUploadComplete={onUploadComplete}
    />
  </ApplicationProvider>
);
  1. 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 ponent and mockedDragger, it prints the ponent log.

Any tips on how to proceed? I have already seen this issue and didn’t help.

Share Improve this question edited Jun 14, 2021 at 19:44 Gabriel Michelassi asked Jun 8, 2021 at 22:06 Gabriel MichelassiGabriel Michelassi 1772 silver badges13 bronze badges 2
  • What will happen when onUploadComplete() is called? I think it would be a success message right? I would remend you to test that success message. – Subrato Pattanaik Commented Jun 12, 2021 at 11:02
  • onUploadComplete will trigger some database stuff, nothing that is very important to the test. I tried logging some messages and it works fine on the browser (when status is 'error' or 'uploading', since it never really pletes the upload. I want to simulate the 'done' status to make sure this callback is always called – Gabriel Michelassi Commented Jun 13, 2021 at 13:03
Add a ment  | 

1 Answer 1

Reset to default 5 +50

The first thing you should change is the return you are doing in your mock. You are returning a new ponent 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("pletes 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.

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:

  1. I have a jest.mock to simulate a dumb request that will always succeed
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 ponent 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 } };
});
  1. The test itself (same file as the mock), where it renders the ponent (where antd will be imported), simulate an upload and then checks if the callback has been called.
it('pletes 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();
  });
  1. elementRenderer
const elementRenderer = ({
  onUploadComplete = () => {}
}) => (
  <ApplicationProvider>
    <UploadCompanyLogo
      onUploadComplete={onUploadComplete}
    />
  </ApplicationProvider>
);
  1. 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 ponent and mockedDragger, it prints the ponent log.

Any tips on how to proceed? I have already seen this issue and didn’t help.

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:

  1. I have a jest.mock to simulate a dumb request that will always succeed
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 ponent 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 } };
});
  1. The test itself (same file as the mock), where it renders the ponent (where antd will be imported), simulate an upload and then checks if the callback has been called.
it('pletes 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();
  });
  1. elementRenderer
const elementRenderer = ({
  onUploadComplete = () => {}
}) => (
  <ApplicationProvider>
    <UploadCompanyLogo
      onUploadComplete={onUploadComplete}
    />
  </ApplicationProvider>
);
  1. 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 ponent and mockedDragger, it prints the ponent log.

Any tips on how to proceed? I have already seen this issue and didn’t help.

Share Improve this question edited Jun 14, 2021 at 19:44 Gabriel Michelassi asked Jun 8, 2021 at 22:06 Gabriel MichelassiGabriel Michelassi 1772 silver badges13 bronze badges 2
  • What will happen when onUploadComplete() is called? I think it would be a success message right? I would remend you to test that success message. – Subrato Pattanaik Commented Jun 12, 2021 at 11:02
  • onUploadComplete will trigger some database stuff, nothing that is very important to the test. I tried logging some messages and it works fine on the browser (when status is 'error' or 'uploading', since it never really pletes the upload. I want to simulate the 'done' status to make sure this callback is always called – Gabriel Michelassi Commented Jun 13, 2021 at 13:03
Add a ment  | 

1 Answer 1

Reset to default 5 +50

The first thing you should change is the return you are doing in your mock. You are returning a new ponent 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("pletes 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.

本文标签: javascriptHow to properly mock antd Upload Dragger with jestStack Overflow