Mocking file input in React TestUtils
Asked Answered
P

1

23

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
Plow answered 20/4, 2016 at 18:40 Comment(2)
I can reproduce the same error simply doing const node = document.getElementsByClassName("close"); TestUtils.Simulate.click(node);, where I have a button with a class="close" on it, and it's the only element/object with that class, and those 2 lines are inside another button event handler I'm using. Been looking around for a solution. Using Chrome ver. 51.0.2704.103, React version 15.3.1.Edna
I solved mine by doing const node = document.getElementById("btnRequest"), which is a button that opens (or closes) a modal window, instead of trying to go after the close button on the modal window, itself. Instead of doing var fileInput = TestUtils.findAllInRenderedTree..., could you do something similar and do it one at a time?Edna
S
1

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
});
Suffuse answered 14/7, 2021 at 12:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.