Problems handling a text_field as clicking on it opens a popup - watir-webdriver

I have problem entering text in a text_field as when the script tries to enter anything in the field, it throws a popup. When manually entering, it does not throw a popup. The html of the text field is as below.
<input name="txtperc" type="text" value="0" maxlength="3" id="txtperc" tabindex="1" class="textBox valid" data-setfocus="true" onchange="return OnChangePercentAssignment('1','1');" onkeypress="return restrictKeyPress(event);" onpaste="cleanText.Wait(this)" style="width:30px;">
My code =
text_field(:percentage ,:id=>'txtperc')
self.percentage = 100
My guess is that the script tries to clear the text field and that is triggering the pop up to fire.
I also tried
text_field(:percentage ,:id=>'txtperc')
self.percentage = 10
browser.alert.ok
self.percentage = 100
Is there an alternate way to set/type into the text_field?

The application behaviour seems a bit strange. You might have to bypass the input element's event by executing javascript to set the field. This assumes that the event's being fired can be ignored.
You could define the page object as:
class MyPage
include PageObject
text_field(:percentage ,:id=>'txtperc')
def percentage=(value)
execute_script("document.getElementById('txtperc').value = '#{value}';")
end
end
And then input the field as normal:
self.percentage = 100

Related

How can I write some javascript to click this "continue" button?

<span id="continue" class="a-button a-button-span12 a-button-primary"><span class="a-button-inner"><input id="continue" tabindex="5" class="a-button-input" type="submit" aria-labelledby="continue-announce"><span id="continue-announce" class="a-button-text" aria-hidden="true">
Continue
</span></span></span>
Above the the HTML from part of a page, which has a 'Continue' button that i'm trying to click, using my script.
So, writing in Javascript, i'm trying to click this button. But nothing I have tried works.
My attempted answer is:
function() {
var goButton = document.getElementById("continue");
goButton.click();},
Why doesn't it work? Help me, please !
You have set the ID of both the span and the input field to "continue". ID's should be unique for a single element. When I enter your code in the browser's console it returns the following:
> var goButton = document.getElementById("continue");
< undefined
> goButton.valueOf()
< <span id="continue" class="a-button a-button-span12 a-button-primary">
You can see the span is the element being selected instead of the input submit button. You should rename either of the 2 elements so both have a unique ID and use that in your script.
Edit: OP mentioned the HTML can not be changed so instead of fixing the use of a not-unique ID this Javascript can be used:
function() {
var continueSpan = document.getElementById("continue");
var goButton = continueSpan.firstElementChild.firstElementChild;
goButton.click();}

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.

knockout.js - help dealing with UI state changes when polling for updates

I'm having a problem losing UI state changes after my observables change and was hoping for some suggestions.
First off, I'm polling my server for updates. Those messages are in my view model and the <ul> renders perfectly:
When my user clicks the "reply" or "assign to" buttons, I'm displaying a little form to perform those actions:
My problem at this point was that when my next polling call returned, the list re-binds and I lose the state of where the form should be open at. I went through adding view model properties for "currentQuestionID" so I could use a visible: binding and redisplay the form after binding.
Once that was complete, the form displays properly on the "current item" after rebinding but the form values are lost. That is to say, it rebinds, rebuilds the form elements, shows them, but any user input disappears (which of course makes sense since the HTML was just regenerated).
I attempted to follow the same pattern (using a value: binding to set the value and an event: {change: responseChanged} binding to update an observable with the values). The HTML fragment looks like this:
<form action="#" class="tb-reply-form" data-bind="visible: $root.showMenu($data, 'reply')">
<textarea id="tb-response" data-bind="value: $root.currentResponse, event: {keyup: $root.responseChanged}"></textarea>
<input type="button" id="tb-submitResponse" data-bind="click: $root.submitResponse, clickBubble: false" value="Send" />
</form>
<form action="#" class="tb-assign-form" data-bind="visible: $root.showMenu($data, 'assign')">
<select id="tb-assign" class="tb-assign" data-bind="value: $root.currentAssignee, options: $root.mediators, optionsText: 'full_name', optionsValue: 'access_token', optionsCaption: 'Select one...', event: {change: $root.assigneeChanged}">
</select>
<input type="button" id="tb-submitAssignment" data-bind="click: $root.submitAssignment, clickBubble: false" value="Assign"/>
</form>
Now, I end up with what seems like an infinite loop where setting the value causes change to happen, which in turn causes value... etc.
I thought "screw it" just move it out of the foreach... By moving the form outside of each <li> in the foreach: binding and doing a little DOM manipulation to move the form into the "current item", I figured I wouldn't lose user inputs.
replyForm.appendTo(theContainer).show();
It works up until the first poll return & rebind. Since the HTML is regenerated for the <ul>, the DOM no longer has my form and my attempt to grab it and do the .appendTo(container) does nothing. I suppose here, I might be able to copy the element into the active item instead of moving it?
So, this all seems like I'm missing something basic because someone has to have put a form into a foreach loop in knockout!
Does anybody have a strategy for maintaining form state inside a bound item in knockout?
Or, possibly, is there a way to make knockout NOT bind anything that's already bound and only generate "new" elements.
Finally, should I just scrap knockout for this and manually generate for "new items" myself when each polling call returns.
Just one last bit of info; if I set my polling interval to something like 30 seconds, all the bits "work" in that it submits, saves, rebinds, etc. I just need the form and it's contents to live through the rebinding.
Thanks a ton for any help!
Well, I figured it out on my own. And it's embarrassing.
Here is a partial bit of my VM code:
function TalkbackViewModel( id ) {
var self = this;
talkback.state.currentTalkbackId = "";
talkback.state.currentAction = "";
talkback.state.currentResponse = "";
talkback.state.currentAssignee = "";
self.talkbackQueue = ko.observableArray([]);
self.completeQueue = ko.observableArray([]);
self.mediators = ko.observableArray([]);
self.currentTalkbackId = ko.observable(talkback.state.currentTalkbackId);
self.currentAction = ko.observable(talkback.state.currentAction);
self.currentResponse = ko.observable(talkback.state.currentResponse);
self.currentAssignee = ko.observable(talkback.state.currentAssignee);
self.showActionForm = function(data, action) {
return ko.computed(function() {
var sameAction = (self.currentAction() == action);
var sameItem = (self.currentTalkbackId() == data.talkback_id());
return (sameAction && sameItem);
}, this);
};
self.replyToggle = function(model, event) {
// we're switching from one item to another. clear input values.
if (self.currentTalkbackId() != model.talkback_id() || self.currentAction() != "reply") {
self.currentResponse("");
self.currentAssignee("");
self.currentTalkbackId(model.talkback_id());
}
My first mistake was trying to treat the textarea & dropdown the same. I noticed the dropdown was saving value & reloading but stupidly tried to keep the code the same as the textarea and caused my own issue.
So...
First off, I went back to the using the $root view model properties for currentAssignee and currentResponse to store the values off and rebind using value: bindings on those controls.
Next, I needed to remove the event handlers:
event: { change: xxxChanged }
because they don't make sense (two way binding!!!!). The drop down value changes and updates automatically by using the value: binding.
The textarea ONLY updated on blur, causing me to think I needed onkeyup,onkeydown, etc. I got rid of those handlers because they were 1) wrong, 2) screwing up the value: binding creating an infinite loop.
I only needed this on the textarea to get up-to-date value updates to my viewmodel property:
valueUpdate: 'input'
At this point everything saves off & rebinds and I didn't lose my values but my caret position was incorrect in the textarea. I added a little code to handle that:
var item = element.find(".tb-assign");
var oldValue = item.val();
item.val('');
item.focus().val(oldValue);
Some browsers behave OK if you just do item.focus().val(item.val()); but i needed to actually cause the value to "change" in my case to get the caret at the end so I saved the value, cleared it, then restored it. I did this in the event handler for when the event data is returned to the browser:
$(window).on("talkback.retrieved", function(event, talkback_queue, complete_queue) {
var open_mappings = ko.mapping.fromJS(talkback_queue);
self.talkbackQueue(open_mappings);
if (talkback_queue) self.queueLength(talkback_queue.length);
var completed_mappings = ko.mapping.fromJS(complete_queue);
self.completeQueue(completed_mappings);
if (self.currentTalkbackId()) {
var element = $("li[talkbackId='" + self.currentTalkbackId() + "']");
if (talkback.state.currentAction == "assign") {
var item = element.find(".tb-assign");
var oldValue = item.val();
item.val('');
item.focus().val(oldValue);
} else {
var item = element.find(".tb-response");
var oldValue = item.val();
item.val('');
item.focus().val(oldValue);
}
}
}
);
So, my final issue is that if I used my observables in my method "clearing" the values when a new "current item" is selected (replyToggle & assignToggle), they don't seem to work.
self.currentResponse("");
self.currentAssignee("");
I cannot get the values to clear. I had to do some hack-fu and added the line below that to just work around it for now:
$(".tb-assign").val("");

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.

focus textbox only if value is empty

I'm currently using the html5 autofocus for a login form. I am looking for a function that will autofocus the username textbox only if empty, and if not empty to autofocus on the next textbox.
With the HTML below
<input name="username" value="a" autofocus="autofocus">
<input name="password" type="password">​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​
You can do something like this with jQuery
var $username = $('[name="username"]');
var $password = $('[name="password"]');
if($username.val()​​​​​​​​​​​​​​​​​​​​​.trim().length > 0){
$password.focus();
}​
Should be that simple. Make sure Javascript is at the bottom of the page or you can use $(document).ready() function to make sure Javascript is run after HTML is rendered.
More details based on additional information
<asp:TextBox ID="UserName" runat="server" autofocus="true" required="true"></asp:TextBox>
The reason it doesn't work for your case is because you don't have an attribute called "name". I think you probably should read a little bit about jQuery selector to understand why. If you use ID, then this is how you would do it.
var $username = $('#UserName');
var $password = $('#password');
if($username.val()​​​​​​​​​​​​​​​​​​​​​.trim().length > 0){
$password.focus();
}​
Of course you now have to match the selector for password so it will actually select password input to set the focus on.
Searches the page for input fields, and forces the focus on the first empty one. We might want to restrict the fields to a given form, and possibly add textareas as well - I'll leave that up yo you though - nothing too hard.
var inputs = document.getElementsByTagName('input'),
i = -1, I = inputs.length,
curr;
for (; ++i < I;) {
curr = inputs[i];
if ( !curr.value.length ) {
curr.focus();
break;
}
}​