Protractor : Selection of an element from Popup value - javascript

We have some textboxes where the value needs to be selected from a popup list. The elements and the popup are like this:
The related html codes are as follows:
<div _ngcontent-c6="" class="col px-0">
<input _ngcontent-c6="" aria-multiline="false" autocapitalize="off"
autocorrect="off" class="form-control ng-dirty ng-valid open ng-touched"
formcontrolname="ESYSITENAME" id="esySiteName" name="" placeholder=""
role="combobox" type="text" autocomplete="off" aria-autocomplete="list"
aria-expanded="true" aria-activedescendant="ngb-typeahead-2-0"
aria-owns="ngb-typeahead-2">
<ngb-typeahead-window class="dropdown-menu show ng-star-inserted"
role="listbox" id="ngb-typeahead-2" style="top: 39px; left: 0px;">
<button class="dropdown-item ng-star-inserted active" role="option"
type="button" id="ngb-typeahead-2-0">
<ngb-highlight _ngcontent-c6="" _nghost-c25="" class="ng-star-inserted">
<span _ngcontent-c25="" class="ngb-highlight ng-star-inserted">
Bull Run Elementary School
</span>
</ngb-highlight>
</button>
</ngb-typeahead-window>
</div>
I have tried to select the Popup element using different strategies like:
let EsyNamePopup = $('#ngb-typeahead-2-0 > ngb-highlight > span');
await helpers.waitForElementVisibility(EsyNamePopup);
await helpers.clickWhenClickable(EsyNamePopup);
// or following way:
let EsyNamePopup = element(by.cssContainingText('span', 'Bull Run Elementary School,));
await EsyNamePopup.click();
None of them worked but got following error messages:
NoSuchElementError: No element found using locator: By(css selector, #ngb-typeahead-2-0 > ngb-highlight > span)
Is there a better strategy to select these elements?

I think you can try a long sleep before click on the pop option. If long sleep can fix your issue, it proves your wait function waitForElementVisibility has issue or you not give sufficient wait time.
await $('input#esySiteName').sendKeys('Bull');
await browser.sleep(15000)
await $('#ngb-typeahead-2-0 > ngb-highlight > span').click()

You could set up breakpoints and debug your code by following the steps in here.
http://www.protractortest.org/#/debugging
Might want to verify if sendkeys will initiate the typeahead popup or if element needs to be focused in order for that.

Related

jQuery event when specific option is selected with Ajax HTML structure

I would like to create a jQuery event when a particular option is selected. I am familiar how to do this with a general dropdown structure, but mine is a but unusual and it doesn't work:
HTML
<span class="input-wrapper">
<select name="country" id="billing_country" class="country_select" autocomplete="country" tabindex="-1" aria-hidden="true">
<option value="" selected="selected">Select a country…</option>
<option value="BE">Belgium</option>
<option value="NL" selected="selected">Netherlands</option>
</select>
<span class="select2-container" dir="ltr" style="width: 100%;">
<span class="selection">
<span class="select2-selection" aria-haspopup="true" aria-expanded="false" tabindex="0" aria-labelledby="select2-billing_country-container" role="combobox">
<span class="select2-selection__rendered" id="select2-billing_country-container" role="textbox" aria-readonly="true" title="Belgium" style="color: rgb(51, 51, 51);">Belgium</span>
</span>
</span>
</span>
</span>
My jQuery attempt:
jQuery('#billing_country').change(function(){
jQuery(document).ajaxComplete( function() {
if( jQuery('#select2-billing_country-container').attr('title') == 'Netherlands'){
jQuery('#billing_address_2_field').addClass('hide'); // random event
}
});
});
The above situation is that the <option> is not changing with the selected="selected" attribute when you select another country. The only thing that changes is the attribute title from #select2-billing_country-container. And when you change the selection Ajax is also involved.
What am I missing or how can this be better?
Update
The above jQuery was in general correct, it had some conflicts with other jQuery functions. The only thing that it made it different, was the fact that on page load the dropdown structure is using the general <select><option> elements, but when selecting a different option it turns into an Ajax request, using the <span>. So above and with Doug's answer gave a full result.
UPDATE
On page load, the select menu is being looked at, not the HTML structure, so on page load you need to have this code:
if( jQuery('#billing_country').val() == 'NL'){
jQuery('#billing_address_2_field').css('display', 'none');
}
and you can leave your change event as you have it on your page now.

How to select an element having SELECT OPTION tags from Dropdown?

I am using WEBDRIVERIO framework. I have a dropdown in this html syntax. I tried to expand it using ID=question-3
After that I tried all the options using SELECT by options, values, text. But as it is obviously not SELECT DROPDOWN, none worked. I tried to click the locator based on the input or label option, but getting "element not visible" error message.
<div id="question-3" class="question ">
<div class="single-dropdown">
<input type="text" name="input-3" class="js-single-selection" placeholder="gender" data-question-id="3" data-index="3" readonly="" style="width: 77px;">
<div class="single-select-container" style="display: none;">
<div class="single-select-item" data-image="">
<label for="option-0-3" data-open="exampleModal1" class="option-0-3-modal">man</label>
<input type="checkbox" id="option-0-3" data-answer-type="single" data-question-id="3" value="man" data-index="3">
</div>
<div class="single-select-item checked" data-image="">
<label for="option-1-3" data-open="exampleModal1" class="option-1-3-modal">woman</label>
<input type="checkbox" id="option-1-3" data-answer-type="single" data-question-id="3" value="woman" data-index="3">
</div>
</div>
</div>
.</div>
Since html shows an input tag. I would suggest to :
1. type the value in that input box (placeholder="gender")
2. wait for options to appear (Label tag)
3. click the appeared item.(Appeared label)
Also if your scenario fails please provide stack trace for the failure.
I suggest clicking the Select Item (the input in this case), then implementing a wait (See docs: http://webdriver.io/api/utility/waitForVisible.html). And then clicking the dropdown item after you have waited for it to be visible on the screen. Like so...
browser.click(selectItem);
browser.waitForVisible(dropdownItem);
browser.click(dropdownItem);

Element not updating error state when value changed via javascript

I have a page with some knockout in it that I am trying to implement validation in. I have an input that I allow the user to change with a drop down like this:
<div class="col-md-6 form-group has-feedback" data-bind="css: { 'has-error': Term.hasError }">
<label for="ddlTerm" class="col-form-label" style="padding: 0;">Term</label>
<div class="input-group input-group-sm">
<input type="text" data-bind="textInput: Term" class="form-control input-sm" id="tbTerm" />
<div class="input-group-btn">
<button type="button" class="btn btn-warning dropdown-toggle" data-toggle="dropdown">Select <span class="caret"></span></button>
<ul class="dropdown-menu pull-right">
<li>12 Months</li>
<li>24 Months</li>
<li>36 Months</li>
<li>48 Months</li>
<li>60 Months</li>
<li>72 Months</li>
<li>84 Months</li>
</ul>
</div>
</div>
<span data-bind='visible: Term.hasError, text: Term.validationMessage' class="help-block"></span>
</div>
And the JavaScript function:
function set_Term(months) {
var termInput = document.getElementById("tbTerm");
termInput.value = months;
};
The value is tied to:
self.Term = ko.observable('').extend({ required: 'Term is required' });
The observable is updated when the value changes, but the error state remains the same and the input stays red and the feedback remains visible. Is there a way to force the input to realize that it's value has been changed and it should clear the error?
I've tried to give the element focus(), but that doesn't work.
The only thing that I have found is to give the containing div and the validation message ids and then do this in the JS function:
$('#tbTermDiv').removeClass('has-error');
$('#tbTermNote').addClass('hidden');
Which visually does the trick - the error message goes away and the text box loses the red, but if the user then empties the box again, the red comes back but the message stays hidden... so not the best solution.
I'd be happy with any kind of solution: knockout, jQuery, pure JS.
Edit: I have noticed that this does not update the observable, so when I change the input via the value property it does not update the observable.
The extender works the same way knockout in general works. It's only aware of changes that are done to observables on a view-model. Updating the value of a control on the UI using jquery won't update the underlying view-model so any dependent knockout code doesn't know to update.
You can change your set_term function to be a function on the view-model, and have it update the underlying observable instead of the control. Then use a data-bind to call the function instead of raw js.
self.set_Term = function(months) {
self.Term(months);
};
...
<li><a data-bind="click: function(){set_Term(84)}">84 Months</a></li>
Here's an updated example: https://jsfiddle.net/jlspake/8ob0gj6q/3/

Remove span tag when something removed

I have an HTML structure and I want to remove an additional item.
<div class="tokenfield form-control">
<input class="pro-credit-user" name="pro-credit-users[]" placeholder="Person or business name" style="position: absolute; left: -10000px;" tabindex="-1" type="text">
<input style="position: absolute; left: -10000px;" tabindex="-1" type="text">
<div class="token invalid">
<span class="token-label" style="max-width: 152px;">dfgdfgdf</span>
×
</div>
<input class="token-input ui-autocomplete-input" autocomplete="off" placeholder="Person or business name" id="1479230390171168-tokenfield" tabindex="0" style="min-width: 60px; width: 1237.4px;" type="text">
</div>
<a class="delete-pro-credit" href="#">Delete</a>
<span class="pro-credit-error" style="color:red;float:right">Sorry, this user cannot be found. We will not be able to link this persons name with an Enjoius account</span>
I have jQuery code also:
$("input.pro-credit-user").last().on('tokenfield:removetoken', function (e) {
$(this).closest('span').remove(); // not working
})
When the user removes the token I also wanted to remove the span which has class 'pro-credit-error'. I have written jQuery but it's not working.
How can I remove that span which contains 'pro-credit-error' class?
closest work by traversing up through its ancestors in the DOM tree. In this case the span you want to remove is not it's ancestor
Though not tested you can try this
$("input.pro-credit-user").last().on('tokenfield:removetoken', function (e) {
$(this).parent().sibling('span.pro-credit-error').remove();
})
Below code works for me:
$("input.pro-credit-user").last().on('tokenfield:removetoken', function (e) {
$(e.relatedTarget).parent().prev().parent().find('span.pro-credit-error:eq(0)').remove();
})
Try this one
$(this).next('span').remove();

Radio Button selection using Javascript executor

I am trying the select the 'Yes' 'No' options in a page,as normal 'Click()' is not working I have used JSExecutor for this! The problem is it is selecting the options, but the value was never sent as input, and the application is throwing error when I tried to continue.
Tried xpath's in diff ways!
WebElement st_1 = driver.findElement(By.id("app-select-no"));
((JavascriptExecutor)driver).executeScript("arguments[0].checked = true;", st_1);
Below is HTML
div class="form-group input-form-group col-xs-6">
<input id="app-select-no" class="form-control ng-pristine ng-untouched ng-valid ng-valid-required" type="radio" ng-value="false" ng-model="openAccount.abc" required="" name="affRadioGroup" value="false" aria-checked="true" tabindex="0" aria-required="false" aria-invalid="false"/>
<label class="field-label-radio text-bold active" ng-class="{active : openAccount.abc==false}" for="app-select-no">
<span translate-once="NO_BUTTON">No</span>
</label>
</div>
Let me see If I understood, you are trying to select "yes" or "no" on that element, but I see that the element you are clicking is an input. You also need to send the "value" to the input besides clicking the element.
Im guessing that your problem is not the click with the executeScript, but you can also try this:
WebElement st_1 = driver.findElement(By.id("app-select-no"));
((JavascriptExecutor)driver).executeScript("arguments[0].click();", st_1 );
And to send a Value to that input, yo need to do this:
driver.findElement(By.id("st_1")).sendKeys("value here");
Hope it helps.
Putting the wait for element before executing the javascript solved the problem. Here is the working code for both using javascript and without javascript(using webelement click):
driver.get("file:///home/sgupta/Desktop/a.html");
WebElement radio = new WebDriverWait(driver, 10).until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("input#app-select-no")));
JavascriptExecutor executor = (JavascriptExecutor)driver.getDriver();
executor.executeScript("arguments[0].checked=true", radio);
Using WebElement method :
driver.get("file:///home/sgupta/Desktop/a.html");
WebElement radio = new WebDriverWait(driver, 10).until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("input#app-select-no")));
radio.click();

Resources