I have a slider menu on my page which appear after an interval of time through an animation. I would to know how can I handle click action without using browser.wait(condition, timeout) since depending on the network traffic to fetch data in a remote database.
The page rendering can take long time thus protractor is triggering timeouts error. I have been trying as bellow to use jQuery in order to wait for all transition and animation event to finish, but it is still not working.
BasePage.prototype.clickMenuButton = function(menuName) {
var link = element(by.linkText(menuName));
browser.ignoreSynchronization = true;
browser.executeScript("jQuery('html > *').one('animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd', function(){})").then(
function() {
link.click();
},
function (error) { console.log("Error : ", error); }
);
}
Does anyone know a way to wait for angularjs to finish rendering and animation without using browser.wait and timeouts?
It's a Protractor "incompatibility" with Angular JS. Most of the people calls this a bug, but nobody is sure about this.
However, if this issue occurs, the Angular and Protractor teams recommends in order to fix the $timeout awaits, to replace the $timeout function with a $interval function. They're almost the same, they both achieve the same thing.
$interval function fixes the protractor $timeout issue.
Official documentation states that You should use the $interval for anything that polls continuously (introduced in Angular 1.2rc3).
In this case, $timeout it's not a good option for Front End Developers(AngularJS developers) neither for JavaScript Automation Developers(Protractor developers).
Another option would be to either use a timeout in your test application, either turn the browser sync off.
Here's my example for ignoreSynchronization function.
browser.ignoreSynchronization = true;
browser.wait(toast.isPresent(), 3000).then(
function(arr) {
if (arr) {
toast.getText().then(function(txt) {
// console.log(txt);
expect(txt).toContain(caption);
});
} else {
toast.getText().then(function(txt) {
console.log('current toast: ' + txt);
console.log('modal not catched. see bug with $timeout \n ' +
caption + " \n" +
"==> Error! NOT PRESENT");
});
}
}
);
browser.ignoreSynchronization = false;
You could also have a look over their official github issue tracker. This page states that if you have a $timeout function, that the developer cannot change this(this seems a bit unlikely, and seems like one of the bad QA/dev relationship) you should use the ignoreSynchronization function.
The main conclusion is that, in order to fix protractor $timeout awaits for Angular the Front End developer should easily replace $timeout with $interval.
Let me know if it helps.
Related
I need to ensure that a certain HTTP request was send successfully. Therefore, I'm wondering if a simple way exists to move such a request into a background service task.
The background of my question is the following:
We're developing a survey application using flutter. Unfortunately, the app is intended to be used in an environment where no mobile internet connection can be guaranteed. Therefore, I’m not able to simply post the result of the survey one time but I have to retry it if it fails due to network problems. My current code looks like the following. The problem with my current solution is that it only works while the app is active all the time. If the user minimizes or closes the app, the data I want to upload is lost.
Therefore, I’m looking for a solution to wrap the upload process in a background service task so that it will be processed even when the user closes the app. I found several posts and plugins (namely https://medium.com/flutter-io/executing-dart-in-the-background-with-flutter-plugins-and-geofencing-2b3e40a1a124 and https://pub.dartlang.org/packages/background_fetch) but they don’t help in my particular use case. The first describes a way how the app could be notified when a certain event (namely the geofence occurred) and the second only works every 15 minutes and focuses a different scenario as well.
Does somebody knows a simple way how I can ensure that a request was processed even when there is a bad internet connection (or even none at the moment) while allowing the users to minimize or even close the app?
Future _processUploadQueue() async {
int retryCounter = 0;
Future.doWhile(() {
if(retryCounter == 10){
print('Abborted after 10 tries');
return false;
}
if (_request.uploaded) {
print('Upload ready');
return false;
}
if(! _request.uploaded) {
_networkService.sendRequest(request: _request.entry)
.then((id){
print(id);
setState(() {
_request.uploaded = true;
});
}).catchError((e) {
retryCounter++;
print(e);
});
}
// e ^ retryCounter, min 0 Sec, max 10 minutes
int waitTime = min(max(0, exp(retryCounter)).round(), 600);
print('Waiting $waitTime seconds till next try');
return new Future.delayed(new Duration(seconds: waitTime), () {
print('waited $waitTime seconds');
return true;
});
})
.then(print)
.catchError(print);
}
You can use the plugin shared_preferences to save each HTTP response to the device until the upload completes successfully. Like this:
requests: [
{
id: 8eh1gc,
request: "..."
},
...
],
Then whenever the app is launched, check if any requests are in the list, retry them, and delete them if they complete. You could also use the background_fetch to do this every 15 minutes.
I've made an aplication with vala where at some point I have to process a lot of files. I've created a window to choose a folder and then I get the paths of files and make some proces on them.
I've added a progress bar to this window to show how many files have been processed but for some reason it remains always empty.
Code about window:
this.files_window = new Gtk.Window();
this.files_window.window_position = Gtk.WindowPosition.CENTER;
this.files_window.destroy.connect (Gtk.main_quit);
// VBox:
Gtk.Box vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 5);
this.files_window.add (vbox);
// Buttons to open and close
Gtk.Button cancel = new Gtk.Button.with_label ("Cancel");
Gtk.Button select = new Gtk.Button.with_label ("Select");
vbox.add (select);
vbox.add (cancel);
// proogress bar
this.progress_bar = new Gtk.ProgressBar();
vbox.add(this.progress_bar);
// conect select to method do_stuff
select.clicked.connect (do_stuff);
this.files_window.show_all ();
As you can see, I connect the button "select" to the method "do_stuff" where I get the paths of selected files and make some process.
I update correctlly the fraction of the progres bar because I've added some prints to know if the value is correct and it is. It's just that the windows is not refreshing, possibly because all the work it is doing with the process of the files. Here is the code about do_stuff() method:
// some proces to get paths of files in the list sfiles
double fraction = 0.0;
this.progress_bar.set_fraction (fraction);
int processed_files = 0;
foreach (string sfile in sfiles) {
do_some_proces_to_file(sfile);
processed_files += 1;
fraction = (double)processed_files/(double)sfiles.length;
this.progress_bar.set_fraction (fraction);
stdout.printf("Real fraction: %f\n", this.progress_bar.get_fraction());
}
The printf shows that the value of the progres bar is being updated but in the window the bar is always empty.
Am I missing something? Is it the correct way to do the progres bar? Should I made another thread to do the stuff?
As #nemequ says, your code is blocking the main loop thread (which handles both user input and scheduling/drawing widget updates), hence it the progress bar is not updated until the method completes.
Using a thread is one way solve the problem, however using threads can lead to a lot of bugs however since it can be difficult to make even simple interactions between threads safe.
An async method avoids this by interleaving the code with the other work being done by the main loop. An async version of your do_stuff() would be pretty straight-forward to write, simply declare it async and put a yield in the for loop somewhere:
public async void do_stuff() {
...
foreach (string sfile in sfiles) {
// all of this is as before
do_some_proces_to_file(sfile);
processed_files += 1;
fraction = (double)processed_files/(double)sfiles.length;
this.progress_bar.set_fraction (fraction);
// Schedule the method to resume when idle, then
// yield control back to the caller
Idle.add(do_stuff.callback);
yield;
}
}
You can then kick it off from your click handler by calling: do_stuff.begin().
Unless there is some relevant code you're not showing, you're blocking the main loop. One option would be to do everything in a thread, and use an idle callback to update the UI. The basic idea is something like:
new GLib.Thread<void*>("file-processor", () => {
foreach (string sfile in sfiles) {
/* do stuff */
GLib.Idle.add(() => {
/* Update progress */
return false;
});
}
return null;
});
Depending on your application you may need to add a mutex to avoid race conditions. You may also need to add some logic for canceling the operation.
A better option might be to use a GLib.ThreadPool. You'd still want to update the UI from an idle callback, but this would allow each task to execute in parallel, which could provide a significant speed-up.
If I were you I'd probably wrap it all up in an async function to keep the API tidy, but you don't really have to.
The problem: If I exit the current ion-view when the on-finite callback hasn't finished processing, the on-infinite callback will start getting called in a loop until the app freezes, even if I'm not in the view anymore where the ion-infinite-scroll was installed.
This was difficult to reproduce, but I have found a way to easily manifest it. Setup the on-infinite callback on the view.
<ion-infinite-scroll
immediate-check="false"
on-infinite="loadMore()"
distance="5%">
</ion-infinite-scroll>
And then use $timeout to artificially slow down the loadMore() function to 2 seconds. Something like this:
$scope.loadMore = function() {
console.log("Loading more...");
$timeout(function() {
$scope.$broadcast('scroll.infiniteScrollComplete');
}, 2000);
};
Now, trigger the infinite scroll, and while your loadMore() function is "processing" for 2 seconds, leave your view. You will see that even that you are in another view, the console will continue to print "Loading more..." every 2 seconds. That is, loadMore() is being called in a loop.
On a real scenario, this bug is causing the app to freeze badly. On some links this issue has been addressed but no solutions (the ionic forum is down atm, btw). Some solutions out there suggest calling infiniteScrollComplete inside an $apply(), but that doesn't solve it and actually triggers a digest already in progress error. Others suggest to use $timeout(...,0) as an alternative to $apply() but still the issue will manifest if, for instance, your loadMore() does async calls to your server, in such case there is still a chance that the user exits the view while loadMore() is busy.
Is there any suggested workaround to this?
Ionic team: this is a bug. And all documentation everywhere about ion-infinite-scroll completely ignores this. There should be, at the very least, a warning. My ionic version 1.7.16.
Try below code for stop load more callback.
html
<ion-view view-title="Search">
<ion-content>
<h1>Search</h1>
<ion-infinite-scroll
immediate-check="false"
ng-if="!noMoreItemsAvailable"
on-infinite="loadMore()"
distance="5%">
</ion-infinite-scroll>
</ion-content>
</ion-view>
Controller
$scope.loadMore = function()
{
$http({
method:'GET',
url:'http://headers.jsontest.com/'
}).then(function(response)
{
console.log(response);
$scope.noMoreItemsAvailable = true;
}, function(response)
{});
}
Try below code for infinite loop functionality
HTML
<ion-infinite-scroll
ng-if="!noMoreDaTa" on-infinite="loadData()"
distance="2%" immediate-check="false">
</ion-infinite-scroll>
Controller
$scope.loadData = function() {
if ($scope.dataRecords.length >= $scope.total_records) {
$scope.noMoreDaTa = true;
}
}
Please let me know, if you need any help :)
I have started doing a POC on Protractor as our e2e automation testing tool.
Our application is designed in angular which makes it a perfect fit.
However, I need to login via google which is a non-angular website and therefore at the start of my test I state
browser.ignoreSynchronization = true;
Then I go to
'https://accounts.google.com/ServiceLogin'
Enter my google credentials and click on signin
At this point I try to go to my application's URL, which is an angular application so I was hoping to turn
browser.ignoreSynchronization = false;
All the the above steps are part of a beforeEach so that I can login before each test
But when I turn ignoreSynchronization to false, all my tests start failing.
On the other hand, if I don't turn it to false, I am compelled to use a lot of browser.sleeps as Protractor is still treating it as a non-angular app and does not wait for angular to load fully
I have also tried to put the ignoreSynchronization = false in each individual test as opposed to beforeEach but even then all my tests start failing.
Below is my beforeEach code
browser.ignoreSynchronization = true;
browser.driver.manage().window().setSize(1280, 1024);
browser.get(googlelogin);
email.sendKeys('username');
next.click();
browser.wait(EC.visibilityOf(pwd), 5000);
pwd.sendKeys('pwd');
signin.click();
browser.ignoreSynchronization = false;
browser.driver.get(tdurl);
Few things to fix:
wait for the "click" to go through
use browser.get() on the Angular Page
Here are the modifications:
signin.click().then(function () {
browser.ignoreSynchronization = false;
browser.get(tdurl);
browser.waitForAngular(); // might not be necessary
});
You may also add a wait with an Expected Condition to wait for the login step to be completed - say, wait for a specific URL, or page title, or an element on the page.
Reconciliation_verifyExapanedDatainExpanedRow: function (HeaderName, texttobepresent) {
browser.waitForAngular().then(function () {
var EC = protractor.ExpectedConditions;
var columnHeaderActive = GUtils.$locatorXpath('//p-datatable//span[contains(text(),"' + HeaderName + '")]/..//span[#class="ui-cell-data"][contains(.,\'' + texttobepresent + '\')]');
browser.wait(EC.presenceOf(GUtils.$element(columnHeaderActive)), GUtils.shortDynamicWait()).then(function () {
console.log('PASS');
}, function (err) {
console.log('FAIL');
});
});
},
I'm using the jQuery Tools tooltip plugin, which is initialized with $('selector').tooltip(). I'd like to call this on any current or future .tooltipper element. I figured that the following would work:
$('.tooltipper').live('ready', function(){
$(this).tooltip()
}
But it was unsuccessful---the ready event did not fire. The same for load. I've read that livequery can produce the result of I'm looking for, but surely there is a way to use jQuery .live() to pull it off, considering the documentation says that it works for all jQuery events, of which I believe ready is one.
Quoted from the jQ API (http://api.jquery.com/live/):
In jQuery 1.3.x only the following JavaScript events (in addition to custom events) could be bound with .live(): click, dblclick, keydown, keypress, keyup, mousedown, mousemove, mouseout, mouseover, and mouseup.
As of jQuery 1.4 the .live() method supports custom events as well as all JavaScript events.
As of jQuery 1.4.1 even focus and blur work with live (mapping to the more appropriate, bubbling, events focusin and focusout).
As of jQuery 1.4.1 the hover event can be specified (mapping to "mouseenter mouseleave").
.live() does not appear to support the ready event.
To add to HurnsMobile's excellent answer; Looking at bindReady(), which is the internal call that jQuery makes to bind to the document load event every time you call $(some_function) or $(document).ready(some_function) we see why we cannot bind to "ready":
bindReady: function() {
if ( readyBound ) {
return;
}
readyBound = true;
// Catch cases where $(document).ready() is called after the
// browser event has already occurred.
if ( document.readyState === "complete" ) {
return jQuery.ready();
}
// Mozilla, Opera and webkit nightlies currently support this event
if ( document.addEventListener ) {
// Use the handy event callback
document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
// A fallback to window.onload, that will always work
window.addEventListener( "load", jQuery.ready, false );
// If IE event model is used
} else if ( document.attachEvent ) {
// ensure firing before onload,
// maybe late but safe also for iframes
document.attachEvent("onreadystatechange", DOMContentLoaded);
// A fallback to window.onload, that will always work
window.attachEvent( "onload", jQuery.ready );
// If IE and not a frame
// continually check to see if the document is ready
var toplevel = false;
try {
toplevel = window.frameElement == null;
} catch(e) { //and silently drop any errors
}
// If the document supports the scroll check and we're not in a frame:
if ( document.documentElement.doScroll && toplevel ) {
doScrollCheck();
}
}
}
To sum it up, $(some_function) calls a function which binds to:
DOMContentLoaded
onreadystatechange (DOMContentLoaded)
window.load / onload
Your best bet would be to bind to those actions that might create new .tooltipper elements, rather than trying to listen for the ready event (which happens only once).
HurnsMobile is right. JQuery live does not support the ready-event.
This is why I created a plugin that combines the two. You register your callback once, and then you will need to call the plugin once for content you add manually.
$.liveReady('.tooltipper', function(){
this.tooltip()
});
Then when creating new content:
element.html(somehtml);
element.liveReady();
or
$('<div class="tooltipper">...').appendTo($('body')).liveReady();
A demo is available here: http://cdn.bitbucket.org/larscorneliussen/jquery.liveready/downloads/demo.html
Check out the introductory post here: http://startbigthinksmall.wordpress.com/2011/04/20/announcing-jquery-live-ready-1-0-release/
Also have a look at http://docs.jquery.com/Plugins/livequery, which listenes for changes on the dom.