Как имитировать загрузку файла через загрузку Ant Design с использованием библиотеки тестирования React?

#reactjs #antd #react-testing-library

#reactjs #antd #react-testing-library

Вопрос:

Я использую компонент загрузки 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. Я попытался настроить как свойства передачи данных, так и файлы.

 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?

Ответ №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() но это только потому, что загрузка вызывает некоторые изменения состояния в моем приложении, вы можете просто использовать выбросить его, конечно

Ответ №2:

Вы можете использовать user-event; это вспомогательная библиотека для тестирования библиотеки, которая обеспечивает более продвинутую симуляцию взаимодействия с браузером, чем встроенный метод 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:

Комбинация двух ответов плюс обработка запросов — это то, что сработало для меня. В этом примере вы не указываете URL-адрес для выполнения запроса POST, но компонент перетаскивания Ant Design имеет эту опцию. Я просто хочу поделиться своим решением этого вопроса дополнительная проблема, которую я обнаружил при использовании action свойства, просто чтобы сэкономить ваше время.

Чтобы протестировать перетаскиватель, я сделал это:

 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 multiple={true} accept={".json"} onChange={onChange} action={"/test"} data-testid={"uploader"}>
  

со свойствами onChange and action вы, вероятно, не получите 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();
});
  

Надеюсь, это кому-то пригодится.