Reputation: 323
I have the below code in a component. I want to test that onSubmit of the form, it calls the this.props.onUpload method in reader. How can I test that? My expect test is not working, I'm guessing it's because the this.props.onUpload is inside reader.onload function?
UploadForm.js
handleSubmit(e) {
e.preventDefault();
var inputData = '';
var file = this.state.file;
if (file) {
var reader = new FileReader();
reader.onload = (function(file) {
return function(e) {
inputData = e.target.result;
this.props.onUpload(inputData);
};
})(file).bind(this);
reader.readAsText(file);
}
}
render() {
return(
<form onSubmit={this.handleSubmit}>
<label> Enter File: <br/>
<input type="file" id="fileinput" onChange={this.handleChange}/>
</label>
<input type="submit" value="Submit" className="btn-upload" />
</form>
);
}
UploadForm.test.js
const mockOnUpload = jest.fn();
const file = new File([""], "filename");
const form = shallow(<UploadForm onUpload={mockOnUpload}/>);
const event = {
preventDefault: jest.fn(),
target: {files : [file]}
};
describe('when clicking `upload-file` button', () => {
beforeEach(() => {
form.find('#fileinput').simulate('change', event);
form.find('form').simulate('submit', event);
});
it('calls the handleSubmit CallBack', () => {
expect(mockOnUpload).toHaveBeenCalledWith(input);
});
});
Upvotes: 1
Views: 5911
Reputation: 1418
Super great start with the mock upload being passed in as a prop and creating a fake event!
I always like running into testing issues like these in my own work because it tells me I have a code smell: if it's not easy to test, it likely means it is harder to predict, harder to debug, harder to explain, &c.
I recommend breaking out your functions to more singled responsibility. As it stands, your handleSubmit
is doing a bit more than just handling submit. It also adds an onload
function to an instance of FileReader and calls readAsText
on that instance.
Your IIFE:
function(file) {
return function(e) {
inputData = e.target.result;
this.props.onUpload(inputData);
};
})(file).bind(this);
could be pulled out to an arrow function (taking care of bind
) on the component:
readerOnLoad = file => (e) => {
this.props.onUpload(e.target.result);
}
(Also, is file
needed as an argument here? Doesn't appear to be used.)
Then handleSubmit can interact with
readerOnLoad` like;
reader.onload = this.readOnLoad(file);
At this point, you can test that:
handleSubmit
calls readerOnLoad
with a file argument if it exists on state.readerOnLoad
, called with a file argument and then an event argument, calls this.props.onLoad
with the event target result
value.If both of these tests pass, you can be confident that your code will work with real events, files, and FileReader instances.
It looks like you already understand how to pass in duck-type arguments (like event
s) that match native/browser objects, so just put those together and enjoy the peace of mind of your nicely collaborating functions!
Upvotes: 5