如何正确测试 React Dropzone onDrop 方法

How to test properly React Dropzone onDrop method

我正在测试 React Dropzone,我需要检查 onDrop 函数。该函数有两个参数(acceptedFiles 和 rejectedFiles)。我正在这样模拟文件:

let image = {
  name: 'cat.jpg',
  size: 1000,
  type: 'image/jpeg'
};

然后在我的测试中,我这样做:

it('should call handleOnDrop with more than 5 acceptedFiles', () => {
    const wrapper = mount(mockComponent());

    for (let index = 0; index < 5; index++) {
      images.push(image);
    }

    wrapper.find(Dropzone).simulate('drop', { dataTransfer: { files: images } });

    expect(setUserNotificationsSpy).toHaveBeenCalledTimes(1);
});

这是我的 onDrop 函数:

const handleOnDrop = (acceptedFiles, rejectedFiles) => {
    if (rejectedFiles && rejectedFiles.length) {
      checkMaxFile(rejectedFiles, maxSize) && setUserNotifications('error_big_image');
    }

    acceptedFiles && acceptedFiles.length <= maxFiles ? onDrop(acceptedFiles) : setUserNotifications('more_than_5');
};

预期的结果是 handleOnDrop returns acceptedFiles 但 returns rejectedFiles 我不知道为什么。

Mime 类型还可以,大小也可以。

这是 react-dropzone 的功能:

  fileAccepted(file) {
      // Firefox versions prior to 53 return a bogus MIME type for every file drag, so dragovers with
      // that MIME type will always be accepted
      return file.type === 'application/x-moz-file' || accepts(file, this.props.accept);
  }

谢谢。

路过时

let image = {
  name: 'cat.jpg',
  size: 1000,
  type: 'image/jpeg'
};

进入

wrapper.find(Dropzone).simulate('drop', { dataTransfer: { files: images } });

它会认为图像未定义或为空。 我能够解决这个问题的方法是

//Create a non-null file
const fileContents = "file contents";
const file = new Blob([fileContents], { type: "text/plain" });

 wrapper.find(Dropzone).simulate("drop", { dataTransfer: { files: [file] } });

这当然是您对纯文本文件的处理方式。对于不同类型的图像,您需要指定图像类型而不是 "text/plain"

我在使用 useDropzone 钩子时 运行 遇到了这个问题。使用

wrapper.find(...).simulate('drop', ...);

不适合我。

相反,我在 input 字段上模拟了 change。这适合我对组件进行单元测试的用例。我不关心测试组件的特定放置功能,因为这超出了对我的组件进行单元测试的范围。假设 react-dropzone 正常运行,我只需要测试我的组件是否正确处理了文件放置事件,我仍然可以通过与 input 字段交互来测试它。而且它具有更通用的良好副作用,以防我将来换掉 dropzone 库。

wrapper.find('input').simulate('change', {
  target: { files },
  preventDefault: () => {},
  persist: () => {},
});

我这样定义 files

const createFile = (name, size, type) => ({
  name,
  path: name,
  size,
  type,
});

const files = [
  createFile('foo.png', 200, 'image/png'),
  createFile('bar.jpg', 200, 'image/jpeg'),
];

同样,它适合我的用例,只创建像这样的模拟文件对象,而不是使用原生 File。如果需要,您可以添加更多属性(例如 lastModifiedDate),但我没有。

如果出于某种原因,您觉得需要创建适当的 File 实例,您也可以这样做:

const createFile = (name, size, type) => {
  // the first arg, [], is the file content
  // it's irrelevant, so I left it blank
  // you can fill it like ['foobar'] or [name] if you want to
  const file = new File([], name, { type });
  Reflect.defineProperty(file, 'size', {
    get() {
      return size;
    }
  });
  return file;
};

由于未设置 path 属性,我在测试过程中遇到了一些问题。检查原生 File 对象的相等性也很麻烦。序列化的文件对象最终会变成 {},这显然没有用。我相信您可以让它工作,但是 IMO,如果可以的话,请避免使用本机 File 对象。在我的测试中使用它们没有任何好处。

如果有人在创建存根文件后遇到任何进一步的问题,我发现还在 dataTransfer 选项中提供了一个类型字段来绕过 react-dropzone 使用的一些检查。我正在使用 RTL,所以 ymmv.

const fakeVideo = new File(['muhaahahah'], 'hello.mp4', { type: 'video/mp4' });

fireEvent.drop(getByTestId('test-input'), {
   dataTransfer: { files: [fakeVideo], types: ['Files'] }
});

我正在使用反应测试库,这对我来说很有效, 我已将测试 ID 附加到从 useDropZone 挂钩中获取 getRootProps 的 div,它不一定必须是 div 它可以是任何容器元素

function dispatchEvt(node: any, type: any, data: any) {
  const event = new Event(type, { bubbles: true });
  Object.assign(event, data);
  fireEvent(node, event);
}
async function flushPromises(rerender: any, ui: any) {
  await act(() => wait(() => rerender(ui)));
}
  const onDropMock = jest.fn();

 it("The on Drop is called on dragging a file ", async () => {
    const file = new File([JSON.stringify({ ping: true })], "fileName.csv", { type: "text/csv" });
    const data = mockData([file]);
    const ui = <MyComponent onDrop={onDropMock} />;
    const { container, rerender } = render(ui);
    const input = screen.getByTestId("testId-for-div");

    dispatchEvt(input, "drop", data);
    await flushPromises(rerender, ui);

    expect(onDropMock).toHaveBeenCalled();
  });

我在官方文档中找到了所有这些信息here