Search code examples
javascriptreactjsreact-hooksreact-testing-library

Unable to write jest test case for file upload onChange event in react


I'm new in writing test case, trying to write for onChange event while uploading File in react. I've gone through some of the links but still couldn't find any answer to it.

Here is the code:

    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <div
        className={`dropzone ${isDragging ? "dragging" : ""}`}
        onDragEnter={handleDragEnter}
        onDragLeave={handleDragLeave}
        onDragOver={handleDragOver}
        onDrop={handleDrop}
      >
        <input
          type="file"
          id="input-file-upload"
          className="inputFile"
          data-testid="input-field"
          onChange={handleFileInputChange}
        />
        <label htmlFor="input-file-upload" id="label-file-upload">
          <div>
            <button
              style={{ display: "block", margin: "auto" }}
              onClick={handleButtonClick}
            >
              Add File
            </button>
            {selectedFile ? (
              <p>Selected File: {selectedFile.name}</p>
            ) : (
              <p>Drag and drop files</p>
            )}
          </div>
        </label>
      </div>
    </div>

OnChange event:

const handleFileInputChange = (e) => {
    const file = e.target.files[0];
    setSelectedFile(file);
    console.log(file);
  };

Test case:

test.only("input field onChange", () => {
  render(<App />);
  expect(screen.getByTestId("input-field")).toBeInTheDocument();
});

Unable to cover onChange event, not sure how to do it (here, input element field is not visible). Here is the link for full code (https://codesandbox.io/s/upbeat-glade-x9h48c?file=/src/tests/index.test.tsx:117-246). Could anyone please help me in this? Thanks in advance!


Solution

  • You can use the upload() API of user-event to upload files and trigger the onChange event handler of <input type='file' /> React built-in component.

    index.jsx:

    import React, { useState } from 'react';
    
    export function App() {
        const [isDragging, setIsDragging] = useState(false);
        const [selectedFile, setSelectedFile] = useState(null);
    
        const handleDragEnter = (e) => {
            e.preventDefault();
            setIsDragging(true);
        };
    
        const handleDragLeave = (e) => {
            e.preventDefault();
            setIsDragging(false);
        };
    
        const handleDragOver = (e) => {
            e.preventDefault();
        };
    
        const handleDrop = (e) => {
            e.preventDefault();
            setIsDragging(false);
    
            const file = e.dataTransfer.files[0];
            setSelectedFile(file);
            console.log(file);
        };
    
        const handleFileInputChange = (e) => {
            const file = e.target.files[0];
            setSelectedFile(file);
        };
    
        const handleButtonClick = () => {
            document.getElementById('input-file-upload').click();
        };
    
        return (
            <div className='App'>
                <div
                    className={`dropzone ${isDragging ? 'dragging' : ''}`}
                    onDragEnter={handleDragEnter}
                    onDragLeave={handleDragLeave}
                    onDragOver={handleDragOver}
                    onDrop={handleDrop}
                >
                    <input
                        type='file'
                        id='input-file-upload'
                        className='inputFile'
                        data-testid='input-field'
                        onChange={handleFileInputChange}
                    />
                    <label htmlFor='input-file-upload' id='label-file-upload'>
                        <div>
                            <button style={{ display: 'block', margin: 'auto' }} onClick={handleButtonClick}>
                                Add File
                            </button>
                            {selectedFile ? <p>Selected File: {selectedFile.name}</p> : <p>Drag and drop files</p>}
                        </div>
                    </label>
                </div>
            </div>
        );
    }
    

    index.test.jsx:

    import '@testing-library/jest-dom';
    import { render, screen } from '@testing-library/react';
    import userEvent from '@testing-library/user-event';
    import React from 'react';
    import { App } from './index';
    
    test.only('input field onChange', async () => {
        render(<App />);
        const input = screen.getByTestId('input-field');
        expect(input).toBeInTheDocument();
        const file = new File(['hello'], 'hello.png', { type: 'image/png' });
    
        await userEvent.upload(input, file);
        expect(screen.getByText('Selected File: hello.png')).toBeInTheDocument();
    });
    

    Test result:

     PASS  stackoverflow/76759159/index.test.jsx (9.761 s)
      ✓ input field onChange (84 ms)
    
    -----------|---------|----------|---------|---------|-----------------------
    File       | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s     
    -----------|---------|----------|---------|---------|-----------------------
    All files  |   54.17 |       75 |   28.57 |   54.17 |                       
     index.jsx |   54.17 |       75 |   28.57 |   54.17 | 8-9,13-14,18,22-27,36 
    -----------|---------|----------|---------|---------|-----------------------
    Test Suites: 1 passed, 1 total
    Tests:       1 passed, 1 total
    Snapshots:   0 total
    Time:        10.089 s
    

    package versions:

    "@testing-library/react": "^11.2.7",
    "@testing-library/user-event": "^14.4.3",
    "@testing-library/jest-dom": "^5.16.5",
    "jest": "^26.6.3",
    "react": "^16.14.0",
    "react-dom": "^16.14.0",