【问题标题】:How to simulate uploading file via Ant Design's Upload using React Testing Library?如何使用 React 测试库通过 Ant Design 的 Upload 模拟上传文件?
【发布时间】:2021-01-10 03:18:21
【问题描述】:

我正在使用 Ant Design 的上传组件。

 <Upload 
     accept=".xlsx" 
     onChange={onImport}
  >
     <Button>Upload</Button>
 </Upload>

我尝试使用什么都不做的 fireEvent.change 来模拟更改:

const inputEl = screen.getByText('Import');
const file = new File(['(⌐□_□)'], 'chucknorris.xlsx');
fireEvent.change(inputEl, { target: { files: [file] } });

我也尝试使用 fireEvent.drop。我尝试设置 dataTransfer 和 files 属性。

Object.defineProperty(inputEl, 'dataTransfer', {
    value: {
        files: [file]
    }
});
Object.defineProperty(inputEl, 'files', {
    value: [file]
});
fireEvent.drop(inputEl);

这会触发上传,但我不断收到以下错误:

Cannot read property 'files' of undefined
  at AjaxUploader._this.onFileDrop (node_modules/rc-upload/lib/AjaxUploader.js:108:63)

如何测试 Ant Design 的 Upload?

【问题讨论】:

    标签: reactjs antd react-testing-library


    【解决方案1】:

    这是我的处理方式。由于input 是隐藏的,并且您不能使用 RTL 查询它,我只是使用document 查询输入,如下所示:

    async simulateFileUpload() {
      const file = new File(['(⌐□_□)'], 'chucknorris.png', { type: 'image/png' });
      const hiddenFileInput = document.querySelector('input[type="file"]') as Element;
    
      await act(async () => {
        fireEvent.change(hiddenFileInput, { target: { files: [file] } });
      });
    }
    

    这不是最漂亮的事情,但它是测试上传功能的唯一方法。
    我将fireEvent 包裹在await act() 中,但这只是因为上传会触发我的应用程序中的一些状态更改,您当然可以使用 throw it away

    【讨论】:

      【解决方案2】:

      您可以使用用户事件;是测试库的配套库,提供比内置 fireEvent 方法更高级的浏览器交互模拟。

      安装:

      npm install --save-dev @testing-library/user-event
      

      用法:

      import React from 'react'
      import { render, screen } from '@testing-library/react'
      import userEvent from '@testing-library/user-event'
      
      test('upload file', () => {
         const file = new File(['hello'], 'hello.png', { type: 'image/png' })
      
          render(
              <div>
                 <label htmlFor="file-uploader">Upload file:</label>
                 <input id="file-uploader" type="file" />
              </div>
          )
          const input = screen.getByLabelText(/upload file/i)
          userEvent.upload(input, file)
      
          expect(input.files[0]).toStrictEqual(file)
          expect(input.files.item(0)).toStrictEqual(file)
          expect(input.files).toHaveLength(1)
      })
      

      【讨论】:

        【解决方案3】:

        这两个答案的组合,加上对请求的处理对我有用。在此示例中,您没有提供执行 POST 请求的 URL,但 Ant Design 的 Dragger 组件提供了此选项。 我只想分享我对这个问题的解决方案 + 我在使用 action 属性时发现的一个额外问题,只是为了节省您的时间。

        为了测试 Dragger,我这样做了:

        test("Dragger functionality", async () => {
          const flushPromises = () => new Promise(setImmediate);
          const file = new File(["{test: 1}"], "test.json", {type: 'application/json'})
          const input = screen.getByTestId("uploader"); 
          // I added the data-testid property to the Dragger element, with the value "uploader"
          await act(async () => {
            fireEvent.change(input, {target: {files: [file]}});
          });
          await flushPromises();
        });
        

        userEvent.upload 方法对我没有用。我一直在使用userEvent 进行所有其他测试,但在这种情况下我无法让它工作。


        额外:如果你像我一样使用 Dragger

        <Dragger multiple={true} accept={".json"} onChange={onChange} action={"/test"} data-testid={"uploader"}>
        

        使用onChangeaction 属性,您可能不会调用onChange。 要使其工作,您必须处理发出的 POST 请求,在本例中为 /test。因此,完整的测试将是:

        import { act, fireEvent, render, screen } from "@testing-library/react";
        import { setupServer } from "msw/node";
        import { rest } from "msw";
        import React from "react";
        import { Upload } from "antd";
        const { Dragger } = Upload;
        
        const onChangeMock = jest.fn();
          
        const server = setupServer(rest.post("/test", (req, res, ctx) => {
          return res(ctx.json({response: "200 OK"}));
        }));
        
        beforeAll(() => server.listen());
        afterEach(() => server.resetHandlers());
        afterAll(() => server.close());
        
        test("Dragger functionality", async () => {
          const flushPromises = () => new Promise(setImmediate);
          
          render (
            <Dragger multiple={true} accept={".json"} onChange={onChangeMock} action={"/test"} data-testid={"uploader"}>
              <p className="ant-upload-drag-icon">
                <InboxOutlined />
              </p>
              <p className="ant-upload-text">{"description"}</p>
              <p className="ant-upload-hint">{"hint"}</p>
            </Dragger>
          );
          
          const file = new File(["{test: 1}"], "test.json", {type: 'application/json'})
          const input = screen.getByTestId("uploader"); 
          // I added the data-testid property to the Dragger element, with the value "uploader"
          await act(async () => {
            fireEvent.change(input, {target: {files: [file]}});
          });
          await flushPromises();
          expect(onChangeMock).toBeCalled();
        });
        

        希望这对某人有用。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-11-10
          • 1970-01-01
          • 2022-01-22
          • 1970-01-01
          • 2021-02-06
          • 2019-10-22
          • 2020-03-22
          • 2021-01-31
          相关资源
          最近更新 更多