I have a test case where I create certain objects through REST APIs. The creation of that object takes about 4-5 mins. I want the test to wait till the creation is complete and validate that the object was created. Is there a way to achieve this long wait with protractor? (I have tried many things but nothing seems to be getting me closer to what I'm trying to achieve)
The reason why I need to validate (apart from making sure it works) is that after that I need to test deletion of that object and don't have a way to make the tests wait till the creation was complete.
Code for test
it('should create object', function (done) {
//create objects (click on submit buttons basically this part works fine)
var addButton = homePage.addButton;
for (var i = 0; i < 2 ; i++){
addButton.get(i).click();
}
// in my page after I click creation it shows a loader for each item and
// till it completes. When it completes it shows the item itself without
// the progress bar
var pendingObject = element.all(by.id('.throbber-loader')).then(function (items) {
return items;
});
while (pendingObject) {
pendingObject = element.all(by.id('.throbber-loader')).then(function (items) {
return items;
});
browser.wait(constants.SLEEP.MS2K);
}
// the pending objects are completed so I should be getting 0 of them
// and 2 created
expect(pendingObject.count()).toEqual(0);
var finishedObj = homePage.getItems;
finishedObj.then(function(items){
expect(pendingObject.count()).toEqual(2);
})
done();
});
Appreciate any pointers.
Related
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.
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>
I've tried this:
browser.wait(function () {
return browser.executeScript('return document.readyState==="complete" &&' +
' jQuery !== undefined && jQuery.active==0;').then(function (text) {
return text === true;
});
}, 30000);
If jQuery.active==0 then page is completely loaded. This should work for sites with JQuery and non angular pages.
However, I have many problems of instability to test for non angular sites.
How to fix this?
By default protractor waits until the page is loaded completely. If you are facing any error then it is because protractor is waiting for the default time to be completed, that you have specified in your conf.js file to wait until page loads. Change the value to wait a for longer time if you think your app is slow -
// How long to wait for a page to load.
getPageTimeout: 10000, //Increase this time to whatever you think is better
You can also increase the defaultTimeoutInterval to make protractor wait a little longer before the test fails -
jasmineNodeOpts: {
// Default time to wait in ms before a test fails.
defaultTimeoutInterval: 30000
},
If you want to wait for any particular element, then you can do so by using wait() function. Probably waiting for last element to load is the best way to test it. Here's how -
var EC = protractor.ExpectedConditions;
var lastElement = element(LOCATOR_OF_LAST_ELEMENT);
browser.wait(EC.visibilityOf(lastElement), 10000).then(function(){ //Alternatively change the visibilityOf to presenceOf to check for the element's presence only
//Perform operation on the last element
});
Hope it helps.
I use ExpectedConditions to wait for, and verify page loads. I walk through it a bit on my site, and example code on GitHub. Here's the gist...
Base Page: (gets extended by all page objects)
// wait for & verify correct page is loaded
this.at = function() {
var that = this;
return browser.wait(function() {
// call the page's pageLoaded method
return that.pageLoaded();
}, 5000);
};
// navigate to a page
this.to = function() {
browser.get(this.url, 5000);
// wait and verify we're on the expected page
return this.at();
};
...
Page Object:
var QsHomePage = function() {
this.url = 'http://qualityshepherd.com';
// pageLoaded uses Expected Conditions `and()`, that allows us to use
// any number of functions to wait for, and test we're on a given page
this.pageLoaded = this.and(
this.hasText($('h1.site-title'), 'Quality Shepherd')
...
};
QsHomePage.prototype = basePage; // extend basePage
module.exports = new QsHomePage();
The page object may contain a url (if direct access is possible), and a pageLoaded property that returns the ExepectedCondition function that we use to prove the page is loaded (and the right page).
Usage:
describe('Quality Shepherd blog', function() {
beforeEach(function() {
// go to page
qsHomePage.to();
});
it('home link should navigate home', function() {
qsHomePage.homeLink.click();
// wait and verify we're on expected page
expect(qsHomePage.at()).toBe(true);
});
});
Calling at() calls the ExpectedCondidion (which can be be an and() or an or(), etc...).
Hope this helps...
i have a form loaded with the user data (from Json Store). When i click the first time, the form show good. But, the second time crash :
Firebug says:
Also crash, when i show other form after this form.
Any ideas ?
Edit for more info:
The code to load form is:
cargarFormularioEdicion: function(idPaciente){
var store = Ext.create('cp.store.form.Paciente',{});
store.load({params:{idUsuario: idPaciente}});
var form = Ext.create('cp.view.form.EditPaciente',{
action: 'bin/paciente/modificar.php'
});
// Ver la forma de pasar este listener al controller
form.on('afterrender',function(form,idPaciente){
form.getForm().loadRecord(store.first());
form.getForm().findField('idUsuario').setValue(idPaciente);
});
var win = Ext.create('cp.view.ui.DecoratorForm',{
aTitle: 'Editar paciente',
aForm: form
});
win.show();
}
Hypothetical solution:
Load the store with async = false.
var store = Ext.create('cp.store.form.Paciente',{});
Ext.apply(Ext.data.Connection.prototype, {
async: false
});
store.load({params:{idUsuario: idPaciente}});
Your code does not guarantee that the store is loaded at the time form.getForm().loadRecord(store.first()); is called. Since it is probably not, somewhere in the processing of loadRecord expects tries to access an undefined variable (as your error message shows) and that crashes the javascript execution. That leaves the form component with a broken internal state, and so everything is ugly from there.
You've kind of found that already by loading your store synchronously, but that's really something you should avoid. It wastes processing time by blocking a thread, and probably freeze your application for a few seconds.
The correct way of handling asynchronous events is to pass them a function that they will call back when there done. Hence the name callback function (and the joke "Hollywood principle"). This is efficient and safe.
Since the callback function can be passed in multiple ways, you need to refer to the doc. Taking your code as example, look at the doc for Ext.data.Store#load. Here's how to fix your code elegantly:
cargarFormularioEdicion: function(idPaciente){
var store = Ext.create('cp.store.form.Paciente');
store.load({
params:{idUsuario: idPaciente}
// here's how to pass a callback to Store#load
// notice you get access to some useful parameters as well!
,callback: function(records, operation, success) {
if (!success) {
// the execution will continue from here when the store is loaded
var form = Ext.create('cp.view.form.EditPaciente',{
action: 'bin/paciente/modificar.php'
});
// Ver la forma de pasar este listener al controller
form.on('afterrender',function(form,idPaciente){
form.getForm().loadRecord(store.first());
form.getForm().findField('idUsuario').setValue(idPaciente);
});
var win = Ext.create('cp.view.ui.DecoratorForm',{
aTitle: 'Editar paciente',
aForm: form
});
win.show();
} else {
// you need to handle that case
}
}
});
}
Hypothetical solution: Load the store with async = false.
var store = Ext.create('cp.store.form.Paciente',{});
Ext.apply(Ext.data.Connection.prototype, {
async: false
});
store.load({params:{idUsuario: idPaciente}});
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;
});
});