Confused about entering text into Input element with React Testing Library - react-testing-library

I've been building React Testing Library (RTL) tests for nearly a year so am confused why I suddenly can't enter text into an input field.
This custom search component must have the HTML structure it does for reasons I won't bother you with:
return (
<div className='input button-container' data-testid='search'>
<input type='search' data-testid='search-input' className='search' />
<span data-testid='search-click' onClick={handleSearchClick}></span>
</div>
);
I've removed some superfluous code for clarity.
Here's what I'm doing in my test:
const searchInput = queryByTestId('search-input');
fireEvent.change(searchInput, { target: { value: 'abcde' }});
This seems correct to me but when I check, the abcde value is not being entered into the input element. Any ideas why not?

I'm not sure that queryBy* queries could work for this. I would suggest to test this (getByTestId) :
const searchInput = getByTestId('search-input');
fireEvent.change(searchInput, { target: { value: 'abcde' }});
This works for sure. Never tested this use case with queryBy* but it might return an array or something not suitable for a fireEvent.

Related

Adding Bootstrap 3 popover breaks JQuery Validation Plugin

I have a form, which I'm validating using JQuery Validation plugin. Validation works file until I add a Bootstrap 3 popover to the text field with name "taskName" (the one being validated) (please see below) . When I add the popover to this text field, error messages are repeatedly displayed every time the validation gets triggered. Please see the code excerpts and screenshots below.
I've been trying to figure out what is happening, with no success so far. Any help will be greatly appreciated. Thanks in advance!
HTLM Excerpt
The popover content
<div id="namePopoverContent" class="hide">
<ul>
<li><small>Valid characters: [a-zA-Z0-9\-_\s].</small></li>
<li><small>Required at least 3 characters.</small></li>
</ul>
</div>
The form
<form class="form-horizontal" role="form" method="post" action="" id="aForm">
<div class="form-group has-feedback">
<label for="taskName" class="col-md-1 control-label">Name</label>
<div class="col-md-7">
<input type="text" class="form-control taskNameValidation" id="taskName" name="taskName" placeholder="..." required autocomplete="off" data-toggle="popover">
<span class="form-control-feedback glyphicon" aria-hidden="true"></span>
</div>
</div>
...
</form>
JQuery Validate plugin setup
$(function() {
//Overwriting a few defaults
$.validator.setDefaults({
errorElement: 'span',
errorClass: 'text-danger',
ignore: ':hidden:not(.chosen-select)',
errorPlacement: function (error, element) {
if (element.is('select'))
error.insertAfter(element.siblings(".chosen-container"));
else
error.insertAfter(element);
}
});
//rules and messages objects
$("#aForm").validate({
highlight: function(element) {
$(element).closest('.form-group').removeClass('has-success').addClass('has-error');
$(element).parent().find('.form-control-feedback').removeClass('glyphicon-ok').addClass('glyphicon-remove');
},
success: function(element) {
$(element).closest('.form-group').removeClass('has-error').addClass('has-success');
$(element).parent().find('.form-control-feedback').removeClass('glyphicon-remove').addClass('glyphicon-ok');
}
});
$('.taskNameValidation').each(function() {
$(this).rules('add', {
required: true,
alphanumeric: true,
messages: {
required: "Provide a space-separated name."
}
});
});
});
Bootstrap 3 popover setup
$('[data-toggle="popover"]').popover({
trigger: "focus hover",
container: "body",
html: true,
title: "Name Tips",
content: function() { return $('#namePopoverContent').html();}
});
The screenshots
First Edit
It seems I did not make my question clear, so here it goes my first edit.
I'm not using the popover to display the error messages of the validation. The error messages are inserted after each of the fields that fail validation, which is precisely what I want. Hence, this question does not seem to be a duplicate of any other question previously asked.
Regarding the popover, I just want to add an informative popover that gets displayed whenever the user either clicks the text field "taskName" or hovers the mouse over it. Its role is completely independent of the validation.
The question is, then, why adding the (independent) popover is making the validation plugin misbehave, as shown in the screenshots.
I had the very same issue a few days ago and the only solution I found was to use 'label' as my errorElement:.
Change the line errorElement: 'span' to errorElement: 'label' or simply removing the entire line will temporarily fix the issue. ('label' is the default. )
I am not completely sure what the JQ validate + BS popover conflict is, but I will continue to debug.
After some debugging I think I found the issue.
Both jQuery validate and bootstrap 3 popovers are using the aria-describedby attribute. However, the popover code is overwriting the value written by jQuery validate into that attribute.
Example: You have a form input with an id = "name", jQuery validate adds an aria-describedby = "name-error" attribute to the input and creates an error message element with id = "name-error" when that input is invalid.
using errorElement:'label' or omitting this line works because on line 825 of jquery.validate.js, label is hard-coded as a default error element selector.
There are two ways to fix this issue:
Replace all aria-describedby attributes with another attribute name like data-describedby. There are 4 references in jquery.validate.js. Tested.
or
Add the following code after line 825 in jquery.validate.js. Tested.
if ( this.settings.errorElement != 'label' ) {
selector = selector + ", #" + name.replace( /\s+/g, ", #" ) + '-error';
}
I will also inform the jQuery validate developers.
The success option should only be used when you need to show the error label element on a "valid" element, not for toggling the classes.
You should use unhighlight to "undo" whatever was done by highlight.
highlight: function(element) {
$(element).closest('.form-group').removeClass('has-success').addClass('has-error');
$(element).parent().find('.form-control-feedback').removeClass('glyphicon-ok').addClass('glyphicon-remove');
},
unhighlight: function(element) {
$(element).closest('.form-group').removeClass('has-error').addClass('has-success');
$(element).parent().find('.form-control-feedback').removeClass('glyphicon-remove').addClass('glyphicon-ok');
}
(The success option could also be used in conjunction with the errorPlacement option to show/hide tooltips or popovers, just not to do the styling, which is best left to highlight and unhighlight.)
Also, I recommend letting the Validate plugin create/show/hide the error label element, rather than putting it the markup yourself. Otherwise, the plugin will create its own and ignore the one you've created.
In case you were unaware, you cannot use the alphanumeric rule without including the additional-methods.js file.

Trouble with helpers and db calls

Just started to learn a Meteor and stuck with a silly problem. I have a collection "Images" and i trying to get one random image from her. But in the browser's console it's says that "Cannot read property 'url' of undefined", but if type a db's "findOne" method in the console there is a record that i wanted.
A client code:
Template.main.helpers({
img: function () {
return Images.findOne({rand: {$gte: Math.random()}}).url;
}
});
Collection:
Images = Meteor.Collection('images');
On a server's side i've got simple fixtures for initial tests
if(Images.find().count() === 0){
Images.insert({url: "http://domain.com/test1.jpg", rand: Math.random()});
Images.insert({url: "http://domain.com/test2.jpg", rand: Math.random()});
Images.insert({url: "http://domain.com/test3.jpg", rand: Math.random()});
}
And simple template:
<head>
<title>Test</title>
</head>
<body>
{{> main}}
</body>
<template name="main">
{{img}}
</template>
P.S. I'm working under the Win 8.
First, I'm going to assume that the images are published to the client. You should verify that Images.find().count() > 0 is true in your browser console (not in your template code).
Next, you should read about how to add guards to your template code to fix the error you are seeing. This article should explain what you need to know.
Finally, unless you actually need rand on your documents, there are better ways to accomplish a random selection. Your question says you are looking for one image, but it's possible that 1 or 0 could be returned with your code. Instead you could use the built-in Random package. Give this a try:
Template.main.helpers({
img: function() {
var image = Random.choice(Images.find().fetch());
return image && image.url;
}
});
Although, I think it makes more sense to return the image object:
Template.main.helpers({
image: function() {
return Random.choice(Images.find().fetch());
}
});
And then select the url in your template (no guards are needed in this case):
<template name="main">
<img src="{{image.url}}">
</template>
You need to be careful with your assumptions when you load a template. When your web page has loaded on the web browser, it won't necessarily have any data in it (your Images.findOne() query could return null)
You just need to take account of this possibility
var image = Images.findOne({rand: {$gte: Math.random()}});
return image && image.url

Javascript focus event goes to next form field

I am fairly new to Javascript and have a basic question. I have an HTML form with first_name and last_name input fields. I have the following Javascript code in the header but after the code runs, the focus goes to the next field (last_name). Why is that and how do I correct it?
Thank you.
<script>
function validateForm()
{
valid = true;
//validate first name
if (document.contactform.first_name.value == "")
{
//alert user first name is blank
alert("You must enter a first name");
document.getElementById("first_name").focus();
return false;
}
return valid;
}
</script>
and the form field code is:
input type="text" name="first_name" id="first_name" maxlength="50" size="30" onBlur="validateForm()"
A fix for this is to add a slight delay.. like so:
setTimeout(function() {
document.getElementById('first_name').focus()
}, 10);
Here is your example with this fix in jsfiddle: http://jsfiddle.net/FgHrg/1/
It seems to be a common Firefox problem.. I don't know exactly why but it has something to do with Firefox loading the javascript before the DOM is fully loaded.. in otherwords getElementById('first_name') returns null. But adding the slight delay fixes this problem.

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.

HTML5 form placeholder fallback with form validation in Prototype

I've got an HTML5 form on my page with an email input that has place holder text in it. It works beautifully and I love the native validation!
I'm not sure how to serve old browsers best. I'm using a bit of javascript that copies the placeholder's text and imprints it as a value. It works well, but then the form validation goes off because there's text that isn't an email address in the form.
I do not want to lose the validation.. Any ideas?
HTML
<input id="email" type="email" placeholder="Enter your email address">
JavaScript (Prototype):
var Placeholder = Class.create({
initialize: function (element) {
this.element = element;
this.placeholder = element.readAttribute('placeholder');
this.blur();
Event.observe(this.element, 'focus', this.focus.bindAsEventListener(this));
Event.observe(this.element, 'blur', this.blur.bindAsEventListener(this));
},
focus: function () {
if (this.element.hasClassName('placeholder'))
this.element.clear().removeClassName('placeholder');
},
blur: function () {
if (this.element.value === '')
this.element.addClassName('placeholder').value = this.placeholder;
}
});
Event.observe(window, 'load', function(e){
new Placeholder($('email'));
});
EDIT:
Wouldn't it be great if browsers supporting placeholder ignored the value attribute?
EDIT 2:
No, I don't want to set the input type to text. That will change the validation's behavior from email syntax to spellcheck.
User Modernizr to detect support for placeholder and only use your javascript to copy the placeholder text if support doesn't exist:
if(!Modernizr.input.placeholder){
// copy placeholder text to input
}
This will prevent it from doing the copy on browsers supporting html5 form attributes like placeholder.
Try this:
<input type="email" value="Enter Email"
onfocus="if (this.value == 'Enter Email') {this.value = '';}"
onblur="if (this.value =='') {this.value = 'Enter Email';}" />
I know it's an old question, but it could help other users that come across this question.
You can use this http://afarkas.github.com/webshim/demos/demos/webforms.html for form validation with support for older browsers. It sits on top of jQuery and Modernizer and is pretty easy to implement.
Hope it helps.