Files upload testing in Enzyme

Thomas John picture Thomas John · Sep 26, 2016 · Viewed 9.9k times · Source

I have a FileInput in my render function

      <FileInput
      accept= "image/jpeg,image/png,audio/mp3"
      onChange= {this.fileInputOnChange}
      children= {<i className="fa fa-paperclip"/>}
      className= 'fileInput'
      />

I need to write a test for file upload, when I simulate the change function it call the function fileInputOnChange

fileInputOnChange: function(evt){
var file = evt.target.files[0];
var fileReader = new FileReader();

fileReader.onload = function(readFile){
  // Check the file type.
  var fileType = file.type.toLowerCase();

  if(fileType.lastIndexOf('image') === 0 && (fileType.lastIndexOf('png') >= 0 || fileType.lastIndexOf('jpeg'))){
    var image = new Image();

    image.onload = function(){
      var attachedFile = {
        attached: file,
        mediaSource: readFile.target.result,
        type: 'image'
      } 
        this.props.onChange(attachedFile);
    }.bind(this);

    image.onerror = function(){
      this.props.onError("INVALID_TYPE");
    }.bind(this);

    image.src = readFile.target.result;
  }else if(fileType.lastIndexOf('audio') === 0 && fileType.lastIndexOf('mp3') >= 0){
    //@todo: manage audio upload here
    var audio = new Audio();

    audio.oncanplaythrough = function(){
      var attachedFile = {
        attached: file,
        mediaSource: readFile.target.result,
        type: 'audio'
      } 
        this.props.onChange(attachedFile);
    }.bind(this);

    audio.onerror = function(e){
      this.props.onError("INVALID_TYPE");
    }.bind(this)

    audio.src = readFile.target.result;

  }else if (fileType.lastIndexOf('video') === 0 && fileType.lastIndexOf('mp4') >= 0){        
    var video = document.createElement('video');

    video.oncanplaythrough = function(){
      var attachedFile = {
        attached: file,
        mediaSource: readFile.target.result,
        type: 'video'
      } 
        this.props.onChange(attachedFile);
    }.bind(this);

    video.onerror = function(){
      this.props.onError("INVALID_TYPE");
    }.bind(this)

    video.src = readFile.target.result;
  }
}.bind(this);

fileReader.onerror = function(){
  this.props.onError("READING_ERROR");
}.bind(this)

fileReader.readAsDataURL(file); }

I could not add any files while simulating the upload button, I am confused of how to write the test for this scenario. Anyone ever came across this kinda scenarios? I would be greatful for all sorta helps.

it('testing attachfile change function...',()=>{
 const wrapper=shallow(<AttachFile />);
 wrapper.find('FileInput').simulate('change');
 console.log(wrapper.debug());
 });

When I tried the above test I got the following error

TypeError: Cannot read property 'target' of undefined
  at [object Object].fileInputOnChange (js/components/home/chats/AttachFile.react.js:11:16)
  at node_modules/enzyme/build/ShallowWrapper.js:768:23
  at ReactDefaultBatchingStrategyTransaction.Mixin.perform (node_modules/react/lib/Transaction.js:138:20)
  at Object.ReactDefaultBatchingStrategy.batchedUpdates (node_modules/react/lib/ReactDefaultBatchingStrategy.js:63:19)
  at batchedUpdates (node_modules/react/lib/ReactUpdates.js:98:20)
  at node_modules/enzyme/build/ShallowWrapper.js:767:45
  at withSetStateAllowed (node_modules/enzyme/build/Utils.js:196:3)
  at ShallowWrapper.simulate    (node_modules/enzyme/build/ShallowWrapper.js:764:42)
  at Context.<anonymous> (test/sample.js:40:27)

Answer

sminutoli picture sminutoli · Sep 27, 2016

You'll need to provide a mocked event object, something like:

wrapper.find('FileInput').simulate('change', {
  target: {
     files: [
       'dummyValue.something'
     ]   
  }
});

Your component its making a lot of work inside, its like a huge side effect (it defines two callbacks with logic nailed). It's going to be difficult, but I suppose you'll need to mock FileReader as well using two spies, one reacting to the readAsDataURL calling the onload and another one calling the onerror.

Then you can check if your callbacks are doing what is supposed to.

Hope it helps!