Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking file input in React TestUtils

I have a component with the following render function:-

  render: function() {
   <input
    type="file"
    name: this.props.name,
    className={this.props.className}
    onChange={this.props.handleChange}
    accept={this.props.accept}/>
 }

State is managed by a container which uploads the file server-side using jquery AJAX call:

 getInitialState: function() {
   return {
     uploaded: false
  };
}

handleChange: function(event) {
event.preventDefault();

var file = event.target.files[0];
if (!file) {
  return;
}

var reader = new FileReader();
reader.readAsText(file, 'UTF-8');
var self = this;

reader.onload = function(e) {
  var content = e.target.result;

 var a =  $.ajax({
    type: 'PUT',
    url: 'http://localhost:8080/upload',
    contentType: 'application/json',
    dataType: "json",
    data: JSON.stringify({
      "input": content
    })
  })
  .fail(function(jqXHR, textStatus, errorThrown) {
    console.log("ERROR WHEN UPLOADING");
  });

 $.when(a).done(function() {
      self.setState({
      uploaded: true,
      });
  });

  } 
} 

This works flawlessly with the server running. However, I'd like to test without the need to invoke the server. Here's the Mocha test I have written so far:

var React = require('react');
var assert = require('chai').assert;
var TestUtils = require('react-addons-test-utils');
var nock = require("nock");
var MyContainer = require('../containers/MyContainer');

describe('assert upload', function () {
  it("user action", function () {

    var api = nock("http://localhost:8080")
        .put("/upload", {input: "input"})
        .reply(200, {
        });

  var renderedComponent = TestUtils.renderIntoDocument(
          <MyContainer />
  );

  var fileInput = TestUtils.findAllInRenderedTree(renderedComponent,
           function(comp) { 
                           return(comp.type == "file");
                          });

 var fs = require('fs') ;
 var filename = "upload_test.txt"; 
 var fakeF = fs.readFile(filename, 'utf8', function(err, data) {
    if (err) throw err;
 });

  TestUtils.Simulate.change(fileInput, { target: { value: fakeF } });

  assert(renderedComponent.state.uploaded === true);
  });
});

Getting this error:-

TypeError: Cannot read property '__reactInternalInstance$sn5kvzyx2f39pb9' of undefined
like image 767
fbielejec Avatar asked Apr 20 '16 18:04

fbielejec


Video Answer


1 Answers

Do you need a 'true' file? I think your issue is in how you're managing your 'fake' file, and your file input's change event. You can use the File Api to 'create' a dummy file, and then you just have to properly set your file input. It doesn't use value, but rather files.

// similar example from KCD https://github.com/testing-library/react-testing-library/issues/93#issuecomment-392126991
const file = new File(['(⌐□_□)'], 'chucknorris.png', { type: 'image/png' });
TestUtils.Simulate.change(fileInput, { target: { files: [ file ] } });

If you actually needed the 'file' (upload_text.txt), then that could be more tricky. Node fs.readFile is async, and the data isn't available outside of the callback, plus you'd have to convert the data into an actual file object to give to the input.

// rough pass at this, you'll have to play with it
import fs from 'fs';
const filename = 'upload_text.txt';
fs.readFile(filename, 'utf8', (err, data) => {
  if (err) throw err;
  const file = new File(data, filename, { type: 'text/plain' });
  TestUtils.Simulate.change(fileInput, { target: { files: [ file ] } });
  // whatever comes next
});
like image 66
Steve -Cutter- Blades Avatar answered Oct 10 '22 17:10

Steve -Cutter- Blades