Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test react-dropzone with Jest and react-testing-library?

I want to test onDrop method from react-dropzone library in React component. I am using Jest, React Testing Library. I'm creating mock file and I'm trying to drop this files in input, but in console.log files are still equal to an empty array. Do you have any ideas?

package.json

"typescript": "^3.9.7",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.0.4",
"@types/jest": "^26.0.13",
"jest": "^26.4.2",
"ts-jest": "^26.3.0",
"react-router-dom": "^5.1.2",
"react-dropzone": "^10.1.10",
"@types/react-dropzone": "4.2.0",

ModalImportFile.tsx

import React, { FC, useState } from "react";
import { Box, Button, Dialog, DialogContent, DialogTitle, Grid } from "@material-ui/core";
import { useDropzone } from "react-dropzone";
import AttachFileIcon from "@material-ui/icons/AttachFile";
import DeleteIcon from "@material-ui/icons/Delete";

interface Props {
    isOpen: boolean;
}

interface Events {
    onClose: () => void;
}

const ModalImportFile: FC<Props & Events> = props => {
    const { isOpen } = props as Props;
    const { onClose } = props as Events;

    const [files, setFiles] = useState<Array<File>>([]);

    const { getRootProps, getInputProps, open } = useDropzone({
        onDrop: (acceptedFiles: []) => {
            setFiles(
                acceptedFiles.map((file: File) =>
                    Object.assign(file, {
                        preview: URL.createObjectURL(file),
                    }),
                ),
            );
        },
        noClick: true,
        noKeyboard: true,
    });

    const getDragZoneContent = () => {
        if (files && files.length > 0)
            return (
                <Box border={1} borderRadius={5} borderColor={"#cecece"} p={2} mb={2}>
                    <Grid container alignItems="center" justify="space-between">
                        <Box color="text.primary">{files[0].name}</Box>
                        <Box ml={1} color="text.secondary">
                            <Button
                                startIcon={<DeleteIcon color="error" />}
                                onClick={() => {
                                    setFiles([]);
                                }}
                            />
                        </Box>
                    </Grid>
                </Box>
            );
        return (
            <Box border={1} borderRadius={5} borderColor={"#cecece"} p={2} mb={2} style={{ borderStyle: "dashed" }}>
                <Grid container alignItems="center">
                    <Box mr={1} color="text.secondary">
                        <AttachFileIcon />
                    </Box>
                    <Box color="text.secondary">
                        <Box onClick={open} component="span" marginLeft="5px">
                            Download
                        </Box>
                    </Box>
                </Grid>
            </Box>
        );
    };

    const closeHandler = () => {
        onClose();
        setFiles([]);
    };

    return (
        <Dialog open={isOpen} onClose={closeHandler}>
            <Box width={520}>
                <DialogTitle>Import</DialogTitle>
                <DialogContent>
                    <div data-testid="container" className="container">
                        <div data-testid="dropzone" {...getRootProps({ className: "dropzone" })}>
                            <input data-testid="drop-input" {...getInputProps()} />
                            {getDragZoneContent()}
                        </div>
                    </div>
                </DialogContent>
            </Box>
        </Dialog>
    );
};

export default ModalImportFile;

ModalImportFile.test.tsx

import React from "react";
import { render, screen, fireEvent } from "@testing-library/react";
import ModalImportFile from "../../components/task/elements/ModalImportFile";

const props = {
    isOpen: true,
    onClose: jest.fn(),
};

beforeEach(() => jest.clearAllMocks());

describe("<ModalImportFile/>", () => {
    it("should drop", async () => {
        render(<ModalImportFile {...props} />);

        const file = new File([JSON.stringify({ ping: true })], "ping.json", { type: "application/json" });
        const data = mockData([file]);

        function dispatchEvt(node: any, type: any, data: any) {
            const event = new Event(type, { bubbles: true });
            Object.assign(event, data);
            fireEvent(node, event);
        }

        function mockData(files: Array<File>) {
            return {
                dataTransfer: {
                    files,
                    items: files.map(file => ({
                        kind: "file",
                        type: file.type,
                        getAsFile: () => file,
                    })),
                    types: ["Files"],
                },
            };
        }
        const inputEl = screen.getByTestId("drop-input");
        dispatchEvt(inputEl, "dragenter", data);
    });
}
like image 369
Евгений Валяев Avatar asked Oct 29 '20 08:10

Евгений Валяев


Video Answer


2 Answers

With the rokki`s answer (https://stackoverflow.com/a/64643985/9405587), I rewrote the test component for easier understanding.

ModalImportFile.test.tsx

import React from "react";
import { render, screen, fireEvent } from "@testing-library/react";
import ModalImportFile from "../../components/task/elements/ModalImportFile";

const props = {
    isOpen: true,
    onClose: jest.fn(),
};

beforeEach(() => jest.clearAllMocks());

describe("<ModalImportFile/>", () => {
    it("should drop", async () => {
        render(<ModalImportFile {...props} />);
        window.URL.createObjectURL = jest.fn().mockImplementation(() => "url");
        const inputEl = screen.getByTestId("drop-input");
        const file = new File(["file"], "ping.json", {
            type: "application/json",
        });
        Object.defineProperty(inputEl, "files", {
            value: [file],
        });
        fireEvent.drop(inputEl);
        expect(await screen.findByText("ping.json")).toBeInTheDocument();
}
like image 184
Евгений Валяев Avatar answered Oct 25 '22 18:10

Евгений Валяев


How about changing fireEvent(node, event); to fireEvent.drop(node, event);.

like image 44
rokki Avatar answered Oct 25 '22 18:10

rokki