Test React Form Submission With Jest & TestUtils - coffeescript

I'm having trouble testing a form submit event using React, TestUtils and Jest.
I have a component that renders a <form> DOM element; the same component also has a method that handles the onSubmit event and logs a statement. My goal is to mock the onSubmit handler and assert that it is called.
form-component.cjsx
module.exports = React.createClass
# Handle form submissions
handleSubmit: (e) ->
console.log 'Make async call'
# Render a form
render: ->
<form onSubmit={#handleSubmit}>
<input type="submit" />
</form>
__tests__/test-form-component.coffee
jest
.dontMock '../form-component'
React = require 'react/addons'
TestUtils = React.addons.TestUtils
FormComponent = require '../form-component'
describe 'FormComponent', ->
it 'creates a log statement upon form submission', ->
# Render a FormComponent into the dom
formInstance = TestUtils.renderIntoDocument(<FormComponent />)
# Mock the `handleSubmit` method
formInstance.handleSubmit = jest.genMockFunction()
# Simulate a `submit` event on the form
TestUtils.Simulate.submit(formInstance)
# TestUtils.Simulate.submit(formInstance.getDOMNode()) ???
# I would have expected the mocked function to have been called
# What gives?!
expect(formInstance.handleSubmit).toBeCalled()
Related Questions:
Test a React Component function with Jest
Test a form with Jest and React JS TestUtils

What seems to be your issue exactly?
React.addons.TestUtils.Simulate.submit() works for me.
If it can help, I was in similar situation and I testing the submit handler this way (using sinon.js, mocha and chai):
var renderDocumentJQuery = $(renderDocument.getDOMNode())
this.xhr = sinon.useFakeXMLHttpRequest();
var requests = this.requests = [];
this.xhr.onCreate = function (xhr) {
requests.push(xhr);
};
renderDocumentJQuery.find('input#person_email').val('test#email.com');
React.addons.TestUtils.Simulate.submit(renderDocumentJQuery.find('form')[0]);
var requestFired = requests[0];
this.xhr.restore();
it('should fire an AJAX with the right params', function(){
assert.equal(requestFired.requestBody,'campaign_id=123&owner_id=456&person%5Bemail%5D=test%40email.com')
});
it('should fire an AJAX with a POST method', function(){
assert.equal(requestFired.method,'POST')
});
it('should fire an AJAX with the correct url', function(){
assert.equal(requestFired.url,'url-for-testing')
});

There's an issue with the way React calls event handlers that causes the original handler function to continue to be called even if you attempt to mock it first.
This can apparently be avoided by switching to the ES6 class syntax to create component classes, but another simple workaround is to have the event handler just call a second function and mock that. For example:
onSubmit: function() {
this.handleSubmit(); // extra fn needed for Jest
},
handleSubmit: function(){
this.setState({
submitted: true
});
}
You would set the form's onSubmit={this.onSubmit} and mock handleSubmit instead of onSubmit. Since this introduces a seemingly unnecessary extra function, if you decide to do this it's probably worth adding a comment to anticipate later attempts to "fix it" which would break the test.

Related

AEM: Handle submit event of a CQ Dialog after validation

I'm trying to run some js code before a AEM CQ Dialog submit event. I achieved it handling next event:
$(document).on("click", ".cq-dialog-submit", function (e) {
e.stopPropagation();
e.preventDefault();
//More code
$(this).closest("form.foundation-form").submit();
});
This works, but is skipping the AEM form validation and lets required fields to go empty.
Any idea to add an action just after the fields are valid, but to control when the submit must go?
Thanks!
You can try to use pre submit hook
$(window).adaptTo("foundation-registry").register("foundation.form.submit", {
selector: "*",
handler: function () {
// custom code
}
});
It will be executed before every TouchUI Dialog submit and after all validations

Reset a Marketo Form upon submission

I have an embedded Marketo form I am using on my site.
When I click submit I want the form to reset to its original state.
What do I need to add to my code for this, and better yet where can I find this in the Marketo documentation?
Here's my current code
<script src="//app-sjg.marketo.com/js/forms2/js/forms2.min.js"></script>
<form id="mktoForm"></form>
<script>
MktoForms2.loadForm("//app-sjg.marketo.com", "819-OWT-537", 1404);
</script>
<script>
MktoForms2.whenReady(function (form){
form.onSuccess(function(values, followUpUrl){
$('#confirmform').modal('show');
return false;
});
});
</script>
The Marketo Form object does not have the reset functionality, but luckily enough, javascript has such a native .reset() method on the HTML form elements. This .reset() method will restore a form element’s default values.
Having said that, the only thing to do within the .onSuccess() callback is to grab the HTML form. Calling the .getFormElem() method of the Marketo Form object, will give us the jQuery wrapped form element, so with form.getFormElem()[0] finally we get the form node, on which we can call .reset().
Here is the sample code:
<script src="//app-lon06.marketo.com/js/forms2/js/forms2.min.js"></script>
<form id="mktoForm"></form>
<script>
// The fourth argument of the `.loadForm()` can be used as an onReady callback.
MktoForms2.loadForm("//app-sjg.marketo.com", "819-OWT-537", 1404, function(form) {
form.onSuccess(function(values, followUpUrl){
// $('#confirmform').modal('show');
console.log(form);
// .getFormElem() returns the jQuery wrapped form element
var formElement = form.getFormElem()[0];
// .reset() is a native javascript method.
formElement.reset();
// If boolean `false` is returned then the visitor
// will NOT be forwarded to the follow up page!
return false;
});
});
</script>
Note: the good thing is, that all the important hidden fields (e.g.: formid and munchkinId) will remain intact.

Register new event listeners in Polymer 1.0 after initial page load

In my Polymer 1.0 app, I have an on-tap function which dynamically adds another button into the page based on a few parameters in the form. The problem is that after adding the element and its event listener to the page, the new button won't actually fire the on-tap event.
JSFiddle for testing: https://jsfiddle.net/dme6tb7z/
index.html
<template is="dom-bind" id="app">
<div id="output"></div>
<paper-button id="myButton" on-tap="_addButton">Add Button</paper-button>
</template>
<script src="app.js"></script>
Here is where I create the new button and give it a listener in JS. Is there some kind of extra step I need to take so that Polymer can "see" the new on-tap event listener?
app.js
app._addButton = function(e) {
var el = document.createElement('paper-button');
el.innerHTML = "New Button";
el.id = "newbutton";
el.addEventListener('on-tap', '_testEvent');
this.$.output.appendChild(el);
// I also tried adding the event listener after appending
// the element to the page, like so...
// this.$.newButton.addEventListener('on-tap', '_testEvent');
};
app._testEvent = function(e) {
console.log(e);
};
EDIT
I'm thinking maybe I need to use something like Polymer.dom(parent).appendChild(node) to keep the two DOMs in sync. I did this in my app.js like so, but the event still doesn't fire.
app.js scope
(function(document) {
'use strict';
var app = document.querySelector('#app');
...
app._addButton = function(e) {
var el = document.createElement('paper-button');
el.innerHTML = "New Button";
el.id = "newbutton";
el.addEventListener('on-tap', '_testEvent');
var parentNode = document.getElementById('output');
Polymer.dom(parentNode).appendChild(el);
};
...
})(document);
Surely, there must be some way to add event listeners after initial page load? I've done this same thing countless times in other setups, but there's something weird going on with Polymer... I understand there are two DOMs to update, but I'm using the Polymer.dom method, so what in the world is preventing this from happening?
More Info
I noticed that after appending the new button to the page, I can successfully run this:
document.getElementById('newbutton').innerHTML = 'Hello World';
whereas this:
document.getElementById('newbutton').addEventListener('on-tap', '_testEvent');
has no effect (and no errors). Isn't that bizarre? Is there some sort of reinvented registration process in Polymer to add event listeners?
Okay, I think I figured it out.
First of all, on-tap in this context should be tap.
Second, the function name from JS should be formatted like this: this.functionName, or app.functionName if your template has an id of "app" and app is defined as var app = document.querySelector('#app');
All together, it looks like this:
var newElement = document.getElementById('newbutton');
newElement.addEventListener('tap', this._testEvent );
This doesn't work in the JS Fiddle for some reason, but does work in a real Polymer environment.

jquery / ajax form not passing button data

I thought the HTML spec stated that buttons click in a form pass their value, and button "not clicked" did not get passed. Like check boxes... I always check for the button value and sometimes I'll do different processing depending on which button was used to submit..
I have started using AJAX (specifically jquery) to submit my form data - but the button data is NEVER passed - is there something I'm missing? is there soemthing I can do to pass that data?
simple code might look like this
<form id="frmPost" method="post" action="page.php" class="bbForm" >
<input type="text" name="heading" id="heading" />
<input type="submit" name="btnA" value="Process It!" />
<input type="submit" name="btnB" value="Re-rout it somewhere Else!" />
</form>
<script>
$( function() { //once the doc has loaded
//handle the forms
$( '.bbForm' ).live( 'submit', function() { // catch the form's submit event
$.ajax({ // create an AJAX call...
data: $( this ).serialize(), // get the form data
type: $( this ).attr( 'method' ), // GET or POST
url: $( this ).attr( 'action' ), // the file to call
success: function( response ) { // on success..
$('#ui-tabs-1').html( response );
}
});
return false; // cancel original event to prevent form submitting
});
});
</script>
On the processing page - ONLY the "heading" field appears, neither the btnA or btnB regardless of whichever is clicked...
if it can't be 'fixed' can someone explain why the Ajax call doesn't follow "standard" form behavior?
thx
I found this to be an interesting issue so I figured I would do a bit of digging into the jquery source code and api documentation.
My findings:
Your issue has nothing to do with an ajax call and everything to do with the $.serialize() function. It simply is not coded to return <input type="submit"> or even <button type="submit"> I tried both. There is a regex expression that is run against the set of elements in the form to be serialized and it arbitrarily excludes the submit button unfortunately.
jQuery source code (I modified for debugging purposes but everything is still semantically intact):
serialize: function() {
var data = jQuery.param( this.serializeArray() );
return data;
},
serializeArray: function() {
var elementMap = this.map(function(){
return this.elements ? jQuery.makeArray( this.elements ) : this;
});
var filtered = elementMap.filter(function(){
var regexTest1= rselectTextarea.test( this.nodeName );
var regexTest2 = rinput.test( this.type ); //input submit will fail here thus never serialized as part of the form
var output = this.name && !this.disabled &&
( this.checked || regexTest2|| regexTest2);
return output;
});
var output = filtered.map(function( i, elem ){
var val = jQuery( this ).val();
return val == null ?
null :
jQuery.isArray( val ) ?
jQuery.map( val, function( val, i ){
return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
}) :
{ name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
}).get();
return output;
}
Now examining the jQuery documentation, you meet all the requirements for it to behave as expected (http://api.jquery.com/serialize/):
Note: Only "successful controls" are serialized to the string. No submit button value is serialized since the form was not submitted using a button. For a form element's value to be included in the serialized string, the element must have a name attribute. Values from checkboxes and radio buttons (inputs of type "radio" or "checkbox") are included only if they are checked. Data from file select elements is not serialized.
the "successful controls link branches out to the W3 spec and you definitely nailed the expected behavior on the spec.
Short lame answer: I think it is teh broken! Report for bug fix!!!
I've run into a rather unusual issue with this. I'm working on a project and have two separate php pages where one has html on the page separate from the php code and one is echoing html from inside php code. When I use the .serialize on the one that has the separate html code it works correctly. It sends my submit button value in its ajax call to another php page. But in the one with the html echoed from the php script I try to do the same thing and get completely different results. It will send all of the other info in the form but not the value of the submit button. All I need it to do is send whether or not I pushed "Delete" or "Update". I'm not asking for help (violating the rules of asking for help on another persons post) but I thought this info might be helpful in figuring out where the break down is occurring. I'll be looking for a solution and will post back here if I figure anything out.

Problem posted data with jQuery submit()

I have the script below. I am trying to POST the data and insert it into a database, the jQuery executes just fine, but does not post anything, the action is working properly because when i post the data without the script, the data posts fine and is inserted into the database fine without any errors, so it seems as if the jquery function is posting nothing. can someone please help?
$('#form').live('submit',function(){
$('#form').fadeOut('slow');
$('#div').append("<h2>submittes</h2>");
return false;
});
<form id="form" method="post" action="execute.php" name="form">
<textarea id="text" name="update"></textarea>
<br>
<input type="submit" value="update" id="update-submit">
</form>
EDIT:
$('#form').live('submit',function(){
var updateTextArea = $('#textarea').val();
$.ajax({
type: "POST",
url: "execute.php",
data: updateTextArea,
success: function() {
$('#form').fadeOut('slow');
$('#div').append("<h2>updated</h2>");
}
});
return false;
});
this is what i have for the ajax, but i am still not having any success.
You don't have any AJAX calls in your javascript. You're just fading out the form, appending an h2, and preventing the default action from occurring (which would be to submit the form normally).
Here's a basic example of how to create a POST ajax request:
$('#form').submit(function(){
$.post($(this).attr('action'), { update: $(this).find('#text).val() }, function(){
// success
});
});
Checkout the jQuery API/Docs for more info on this. There are also dozens of tutorials lurking around the net on how to do this.
Well, by returning false from the event handler function, you trigger two things:
prevent the default action (.preventDefault())
stop the event propagation (.stopPropagation())
This prevents that the submit ever happens.
You need to transfer the data on your own within the submit event handler. For instance, create an ajax request which serializes the form data and sends it to your server.
Could look like:
$('#form').live('submit',function(){
$('#form').fadeOut('slow');
$('#div').append("<h2>submittes</h2>");
$.post('execute.php', $(this).serialize(), function(data) {
// do something after success
});
return false;
});