For/While loop struggles with async calls - protractor

am trying to check element on regular interval and click only if it is present. Some times this element appears in 2-3 minutes. If it is not present, i want to wait for few seconds and then refresh page
Here is what i tried:
for(var i = 1; i < 60; i++){
element(that.proposalByOrderPath(num)).isPresent().then(function(result){
if(result){
console.log(i);
return element(that.proposalByOrderPath(num)).click();
}
else{
browser.sleep(15000);
browser.refresh();
}
});
}
As an output, it prints 60 twice. It clicks on the element once but tries to look again for the element and throws "element not visible" error.

This is a closure issue. For more understanding read the following-
How do JavaScript closures work?
http://www.w3schools.com/js/js_function_closures.asp

We can solve async call in for loop with any of following 3 ways:
Callback
var a = function(callback){
//code
callback();
}
var b = function(){
for(loop details){
that.a(function(){
})
}
}
By recursive call of function instead of using for loop
By self iteration
for(loop details){
(function(i){
//your code
})(i);
}

Related

If Condition is not invoking inside

My if condition in the following code is not working ....
I thought my if condition would be wrong but then the code should have invoked the else condition but it is not doing soo.
class polyline {
List<LatLng> _listltlg=[];
start_record() {
BackgroundLocation.startLocationService();
BackgroundLocation.getLocationUpdates((location) {
if(_listltlg.isEmpty){
print('2nd if statement');
_listltlg.add(LatLng(location.latitude, location.longitude));
}
else{
print('Else case');
}
});
}
It is correct, the first time the callback is called, the list "_listltlg" IS empty.

How to select a drop down using protractor?

Can any one help me on how to select a drop down in protractor.
Page Object code
function selectDropdownbyNum(element, optionNum) {
if (optionNum) {
element.all(by.tagName('option')).then(function(options) {
browser.sleep('5000');
options[optionNum].click();
console.log('Desired value selected');
});
}
}
var pageName= function(){
this.selectTier = async function(){
var Tiers = element(by.xpath(/*element value*/));
console.log('select silver method');
browser.sleep(5000);
selectDropdownbyNum(Tiers,2);
console.log('value selected');
};
};
module.exports = new pageName();
And Spec is as follows
it('select Silver Tier',async function(){
browser.ignoreSynchronization = true;
console.log('Executing silver tier selection test case');
await pageName.selectTier()
});
I have tried the above code. I am able to print all the values of the drop down, but am unable to click.
Is their any mistake in the above code.I can print the 'Desired value selected'. But value was not selected
May this will help you for selecting option
element(by.cssContainingText('option','Option value')).click();
or
element(by.id('id')).sendKeys("Values from option");
this worked for me
Try:
var Tiers = element(by.xpath(dropDownValue));
Tiers.click();
selectDropdownbyNum(element, optionNum) {
if (optionNum) {
element.all(by.tagName('option')).then(function(options) {
options[optionNum].click();
});
}
}
selectDropdownbyNum(Tiers,4)
Note:
avoid using Xpath example use :
element(by.css('select[formcontrolname="any value according to situation"]'));
I haven't tested it, but I suppose it's because of the nested promise you are using inside the for loop. The nature of the promise is to be async, and the for loop is synchronous, which results in the loop complete whyle the very first promise items[i].getText().then get's resolved and that's why your click didn't succeed. If you don't need to know the option names, then just remove the nested promise items[i].getText() and just execute the click in the loop.

RxJs Observable with infinite scroll OR how to combine Observables

I have a table which uses infinite scroll to load more results and append them, when the user reaches the bottom of the page.
At the moment I have the following code:
var currentPage = 0;
var tableContent = Rx.Observable.empty();
function getHTTPDataPageObservable(pageNumber) {
return Rx.Observable.fromPromise($http(...));
}
function init() {
reset();
}
function reset() {
currentPage = 0;
tableContent = Rx.Observable.empty();
appendNextPage();
}
function appendNextPage() {
if(currentPage == 0) {
tableContent = getHTTPDataPageObservable(++currentPage)
.map(function(page) { return page.content; });
} else {
tableContent = tableContent.combineLatest(
getHTTPDataPageObservable(++currentPage)
.map(function(page) { return page.content; }),
function(o1, o2) {
return o1.concat(o2);
}
)
}
}
There's one major problem:
Everytime appendNextPage is called, I get a completely new Observable which then triggers all prior HTTP calls again and again.
A minor problem is, that this code is ugly and it looks like it's too much for such a simple use case.
Questions:
How to solve this problem in a nice way?
Is is possible to combine those Observables in a different way, without triggering the whole stack again and again?
You didn't include it but I'll assume that you have some way of detecting when the user reaches the bottom of the page. An event that you can use to trigger new loads. For the sake of this answer I'll say that you have defined it somewhere as:
const nextPage = fromEvent(page, 'nextpage');
What you really want to be doing is trying to map this to a stream of one directional flow rather than sort of using the stream as a mutable object. Thus:
const pageStream = nextPage.pipe(
//Always trigger the first page to load
startWith(0),
//Load these pages asynchronously, but keep them in order
concatMap(
(_, pageNum) => from($http(...)).pipe(pluck('content'))
),
//One option of how to join the pages together
scan((pages, p) => ([...pages, p]), [])
)
;
If you need reset functionality I would suggest that you also consider wrapping that whole stream to trigger the reset.
resetPages.pipe(
// Used for the "first" reset when the page first loads
startWith(0),
//Anytime there is a reset, restart the internal stream.
switchMapTo(
nextPage.pipe(
startWith(0),
concatMap(
(_, pageNum) => from($http(...)).pipe(pluck('content'))
),
scan((pages, p) => ([...pages, p]), [])
)
).subscribe(x => /*Render page content*/);
As you can see, by refactoring to nest the logic into streams we can remove the global state that was floating around before
You can use Subject and separate the problem you are solving into 2 observables. One is for scrolling events , and the other is for retrieving data. For example:
let scrollingSubject = new Rx.Subject();
let dataSubject = new Rx.Subject();
//store the data that has been received back from server to check if a page has been
// received previously
let dataList = [];
scrollingSubject.subscribe(function(page) {
dataSubject.onNext({
pageNumber: page,
pageData: [page + 10] // the data from the server
});
});
dataSubject.subscribe(function(data) {
console.log('Received data for page ' + data.pageNumber);
dataList.push(data);
});
//scroll to page 1
scrollingSubject.onNext(1);
//scroll to page 2
scrollingSubject.onNext(2);
//scroll to page 3
scrollingSubject.onNext(3);
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.1.0/rx.all.js"></script>

Protractor: browser.wait function, isElementPresent times out

Here is my code. For some reason it cannot detect the element present, and just times out. Site is in angular. I have tried isPresent, as well as ExpectedConditions and it times out nonetheless. For some reason, it just cannot detect the element no matter how I try to locate it. I have tried multiple elements as well. I'm open to any ideas.
browser.wait(function()
{
return browser.isElementPresent(by.xpath('//[#id="ngdialog1"]/div[2]/div/div')).then(function(present)
{
console.log('\n' + 'looking for element')
if(present)
{
console.log('\n' + 'recognized dialog');
var jccSelect = element(by.xpath('//*[#id="ghId_GameSelectBottomRow"]/div[1]'));
jccSelect.click();
return true;
}
})}, 50000);
});
You have kept return statement in if(present){return true;}, if present value is false then control will not be return, that's why your getting timed out issue.
I have rearranged the code as below:
EC = protractor.ExpectedConditions;
targetElement=element(by.xpath('//[#id="ngdialog1"]/div[2]/div/div'));
browser.wait(function(){
return EC.visibilityOf(targetElement).call().then(function(present){
console.log('\n' + 'looking for element')
if(present)
{
//do what would you like to do
return true;
}
else{
//do what would you like to do
return false;
}
});
}, 50000);

Why aren't these two functions toggling on click event?

I'm trying to toggle two functions. When user clicks the pause button, the input fields are disabled, the label is text is changed to grey and the button changes to a different image. I thought I could use .toggle(), but I can't get the two functions to work either -- only the first one function runs (pauseEmailChannel();), not both on toggle click. I found the even/odd clicks detection script here on SO, but that is not "toggling" these two functions on the click event. My code may be ugly code, but I'm still learning and wanted to show how I am thinking -- right or wrong. At any rate, can someone give me a solution to how to do this? I didn't think it would be too difficult but I'm stuck. Thanks.
HTML
jQuery
$(".btn_pause").click(function(){
var count = 0;
count++;
//even odd click detect
var isEven = function(num) {
return (num % 2 === 0) ? true : false;
};
// on odd clicks do this
if (isEven(count) === false) {
pauseEmailChannel();
}
// on even clicks do this
else if (isEven(count) === true) {
restoreEmailChannel();
}
});
// when user clicks pause button - gray out/disable
function pauseEmailChannel(){
$("#channel-email").css("color", "#b1b1b1");
$("#notify-via-email").attr("disabled", true);
$("#pause-email").removeClass("btn_pause").addClass("btn_disable-pause");
}
// when user clicks cancel button - restore default
function restoreEmailChannel(){
$("#channel-email").css("color", "#000000");
$("#notify-email").attr("disabled", false);
$("#pause-email").removeClass("disable-pause").addClass("btn_pause");
$("input[value='email']").removeClass("btn_disable-remove").addClass("btn_remove");
}
try this code. It should work fine, except that I could make a mistake when it is even and when odd, but that should be easy to fix.
$(".btn_pause").click(function(){
var oddClick = $(this).data("oddClick");
$(this).data("oddClick", !oddClick);
if(oddClick) {
pauseEmailChannel();
}
else {
restoreEmailChannel();
}
});
The count variable is initialized and set to 0 every time .btn_pause is clicked. You need to move the variable to a higher scope.
For example,
$(".btn_pause").each(function(){
var count = 0;
$(this).click(function(){
count++;
...
});
});
In this way count is initialized only once and it is accessible in the click event handler.
As an alternative way you can also use:
$(".btn_pause").each(function(){
var count = 0;
$(this).click(function(){
[restoreEmailChannel, pauseEmailChannel][count = 1 - count]();
});
});
If the previous construct was too abstract, a more verbose one will look like this:
$(".btn_pause").each(function(){
/* Current element in the array to be executed */
var count = 0;
/* An array with references to Functions */
var fn = [pauseEmailChannel, restoreEmailChannel];
$(this).click(function(){
/* Get Function from the array and execute it */
fn[count]();
/* Calculate next array element to be executed.
* Notice this expression will make "count" loop between the values 0 and 1.
*/
count = 1 - count;
});
});