1

I have an file input element which is bound to a ref variable. Based on the files uploaded, in the onChange event, the file contents are processed . Currently I am writing unit test cases to test this functionality.

App.js

export class Dashboard extends React.Component {
constructor(props) {
    this.uploadFile = React.createRef();
    //Constructing...
  }

readFileContents() {
    const files = this.uploadFile.current.files;

    for (let key in files) {
      if (files.hasOwnProperty(key)) {
        const file = files[key];
        const reader = new FileReader();
        let settings;

        // eslint-disable-next-line no-loop-func
        reader.onload = e => {
          const extension = file.name.split('.')[1];
          //OnLoad Handler
        };

        console.log(this.uploadFile.current.files)
        reader.readAsText(file); //TypeError: Failed to execute 'readAsText' on 'FileReader': parameter 1 is not of type 'Blob'.
      }
    }
  };

render() {
    return (
      <div className="dashboard wrapper m-padding">
        <div className="dashboard-header clearfix">
          <input
            type="file"
            ref={this.uploadFile}
            webkitdirectory="true"
            mozdirectory="true"
            hidden
            onChange={this.readFileContents}
            onClick={this.reset}
          />
          <Button
            outline
            className="toggle-btn float-right"
            onClick={this.openFileDialog}
          >
            Choose folder
          </Button>
        </div>
      </div>
    );
  }
}

I started off with this stack overflow answer and was able to mock the FileReader.

I initially thought simulating the change event with the target files as below, will automatically reflect on the values for this.uploadFile .

const file = new Blob([fileContents], {type : 'application/json'}); 
var event = {"target": {"files": []}};
event.target.files.push(file);

DashboardWrapper.find('input').first().simulate('change', event);

But the behaviour wasnt as I expected and got the below error.

TypeError: Failed to execute 'readAsText' on 'FileReader': parameter 1 is not of type 'Blob'.

Following this I have been trying to change the files key in the ref variable directly from the test file, with no results and the same error.

I would like to first understand if my approach is right. If not, what is the right way to do it?

Juan Rivas
  • 585
  • 1
  • 3
  • 17
Jeyashree .A
  • 17
  • 1
  • 8

1 Answers1

0

As far as I can understand, testing the actual file upload is not recommended in a unit test. After all, these inputs should be thoroughly tested already.

That being said, I had a similar requirement and I solved it like so (I am using VueJS and Jest, but the approach should be similar):

Code:

<img v-if="showLogo && currentFile" class="image-preview" :src="currentFile"/>
<input
   class="file-input"
   type="file"
   ref="fileInput"
   @change="handleFileUpload()"/>

Test:

it('should render the logo if it got uploaded', async () => {
    const wrapper = shallowMount(ApplicationLogoUpload, {
        store,
        localVue,
        propsData: {
            showLogo: true
        }
    });
    const fileInput = wrapper.find('.file-input');
    const mockedGet = jest.fn();
    mockedGet.mockReturnValue(['file1']);
    Object.defineProperty(fileInput.element, 'files', {
        get: mockedGet
    });
    fileInput.trigger('change');
    const imagePreview = wrapper.find('.image-preview');
    expect(imagePreview.attributes().src).toEqual('file1');
});

Most importantly, I mocked the uploaded files using

const mockedGet = jest.fn();
mockedGet.mockReturnValue(['file1']);
Object.defineProperty(fileInput.element, 'files', {
    get: mockedGet
});

I trigger the upload by calling fileInput.trigger('change'); Afterwards, the assertion can be done: src being equal to the mocked file.

Patrick
  • 1,728
  • 2
  • 17
  • 30