How can I test if a html input type radio is checked - angular-spectator

I have this HTML in my component.html:
<input type="radio" [checked]="selected" (change)="select()" />
How can I make a Spectator query and expect to test if this input element is checked or not?
I have tried with:
expect(spectator.query('input')).toHaveAttribute('checked');
But I get the error:
Error: Expected element to have attribute 'checked', but had 'undefined'
And I have tried with:
expect(spectator.query('input')).toBeChecked();
But then I get the error:
Error: Expected element to be checked
How can I test this simple HTML input element?
Thank you
Søren

expect(spectator.query('input')).toBeChecked(); is the correct usage.
It looks like selected property is false due to which radio button is not selected and you are getting this error. Simple fix you binding in test (by setting selected to true) or update assertion to check if radio button is not selected:
expect(spectator.query("input[type=radio]")).not.toBeChecked();
Take a look at this stackblitz code sample where I have 2 bound radio buttons one selected and another not selected and I have tests for it.
it("should be checked", () => {
spectator = createComponent();
expect(spectator.query("#r1[type=radio]")).not.toBeChecked();
});
it("should not be checked", () => {
spectator = createComponent();
expect(spectator.query("#r2[type=radio]")).toBeChecked();
});
Also, take a look at this guide to see available custom matchers.

Related

How to simulate ionBlur in tests

Maybe I'll start with what I want to achieve: I have a form with a required field. By default it should not display any error. The error should be displayed if a user touches the field. So my field looks more or less like this:
<ion-input .... (ionBlur)="updateDispayedErrors()"></ion-input>
But I don't know how to test it because:
Running fixture.debugElement.nativeElement.blur() does not triggers ionBlur handler (the same with ....dispatchEvent(new Event('blur')))
Plain angular (blur) does not work (i.e. if I change the code to (blur)="updateDisplayErrors()" then it does not work)
It seems that calling blur() method on native <input .../> element that is created in the browser would work but... the problem is that when I run the tests fixture.debugElement.nativeElement.childNodes is empty... So the native <input .../> is not created
Please let me know if you would like to see a full example to illustrate it.
If you add a selector to ion-input like:
<ion-input .... (ionBlur)="updateDisplayedErrors()" id="specialInput"></ion-input>
Then you can use fixture.debugElement.triggerEventHandler:
import { By } from '#angular/platform-browser';
...
it('should emit ionBlur', () => {
const ionDe = fixture.debugElement.query(By.css('#specialInput'));
const ionBlurResult = spyOn(component, 'updateDisplayedErrors');
ionDe.triggerEventHandler('ionBlur', {});
expect(ionBlurResult).toHaveBeenCalled();
});

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.

Capturing changes to model using angular-google-places-autocomplete

I'm using angular-google-places-autocomplete (https://github.com/kuhnza/angular-google-places-autocomplete) with Ionic but having problems capturing the selected option when using this directive.
I have the directive set up like this:
<!-- template -->
<input type="text" placeholder="Place search" g-places-autocomplete ng-model="locationSearchResult"/>
<h5>Result</h5>
<pre ng-bind="locationSearchResult | json"></pre>
My controller code is set up to watch for changes to the locationSearchResult model, and if it does change to save the new location to local storage:
// Controller
$scope.locationSearchResult = {};
$scope.$watch('locationSearchResult', function(newVal, oldVal) {
if (angular.equals(newVal, oldVal)) { return; }
$scope.$storage.loc = newVal;
$state.go('new-page');
});
When using the autocomplete it seems to work as expected - I get a list of predictions, and selecting a prediction from the list of predictions updates the text input with the name of the selected place, and the JSON data for the selected place displays under the result heading. But, the change doesn't seem to be picked up by the $scope.$watch in the controller.
As a result, I can't seem to be able to capture the search result data and do anything with it - like add it to the user session.
Maybe I'm just going about it the wrong way (though I used the same approach with ngAutocomplete and it worked ok).
Use the event that gets emitted in your controller.
$scope.$on('g-places-autocomplete:select', function (event, param) {
console.log(event);
console.log(param);
});

Radio button in Lift?

I'm trying to render a radio button choice in my webapp but I have some problems.
What I have done is to try jcern solution posted in this answer:
get checkbox and radio button value in lift
This is my HTML code:
<div class="lift:Radio.render">
<input id="choice" name="choice" type="radio"/>
</div>
And this is my SCALA code:
object Radio {
def render = {
val radioChoices = List("Choice_1", "Choice_2")
var choice:Box[String] = None
"name=choice" #> SHtml.radio(radioChoices, choice, (resp) => choice = Full(resp))
}
}
But the compiler give me an error on binding:
could not find implicit value for parameter computer:net.liftweb.util.CanBind[net.liftweb.http.SHtml.ChoiceHolder[String]]
[error] "#choice" #> SHtml.radio(radioChoices, choice, (resp) => choice = Full(resp))
[error] ^
I have to bind with .toForm to pass compile like this:
"name=choice" #> SHtml.radio(radioChoices, choice, (resp) => choice = Full(resp)).toForm
The problem is that no radio buttons are displayed on my web page, nothing at all..
Am I doing something wrong? I can't see it. And why the first solution (without .toForm) give me an error at compile?
The reason is that SHtml.radio and SHtml.radioElem return a ChoiceHolder instead of a the NodeSeq that most other items in SHtml do - as you can see in the API Doc. Because of that, you need to call .toForm or render the output yourself.
Returning the ChoiceHolder allows you to customize how each item is displayed, so you can add custom text for the label, etc... If the basic toForm output didn't work for you, you could do something like:
val choices = SHtml.radio(radioChoices, choice, (resp) => choice = Full(resp))
"#choice" #> choices.items.zipWithIndex.map { case(itm, pos) =>
val rId = "myId-%s".format(pos)
<div class="radio">{
//We'll add a custom ID attribute to the radio button
itm.xhtml.asInstanceOf[Elem] % ("id", rId)
}<label for={ rId }>{
//Output the toString or use some other method to output a label
itm.toString
}</label>
</div>
}
You can find more information on customizing the radio buttons in the Lift Cookbook.
As for why your CSSSelector is not outputing anything, I am not sure. The code snippet you were trying worked for me. The screenshot below illustrates the output I see with the toForm method.

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.