While loop in nightwatch.js - loops

I'm trying to run the following code (a number of steps) several times (e.g: 10 times) so that:
the user navigates to the detailed url
a random button is clicked, as per the defined var values
this is repeated 10 times
the test is then complete
I'm working with the following NightwatchJS code:
var randomEmail = faker.internet.email()
var competitionReference = ['drawing_21715','drawing_21704']
var randomCompetitionReference = competitionReference[Math.floor(Math.random()*competitionReference.length)]
module.exports = {
'navigate to homepage': function (browser) {
browser
.url('http://clickswin-stage.bellamagazine.co.uk/')
},
'select a competition': function (browser) {
browser
.useXpath()
.click('//*[#id="' + randomCompetitionReference + '"]/div/div[1]')
},
};
I've read that the best way to do this would be to use a while loop, but I'm not really sure how to set this up for my code above.
For example, if I were to use:
var i = 0
while ( i < 10) {
etc, whereabouts would I need to put this loop code within my code above?
Any help would be greatly appreciated.

One solution could be using a recursive function. Here is an example of how this could look like:
var randomEmail = faker.internet.email()
var competitionReference = ['drawing_21715', 'drawing_21704']
// var randomCompetitionReference = competitionReference[Math.floor(Math.random() * competitionReference.length)]
var randomCompetitionReference = function() {return competitionReference[Math.floor(Math.random() * competitionReference.length)]}
module.exports = {
'navigate to homepage': function (browser) {
browser
.url('http://clickswin-stage.bellamagazine.co.uk/')
},
'select a competition': function (browser, recursions) {
// Put the main code into a separat recursive function.
const doClick = function(times) {
if (times > 0) { // This is the equivalent to "while ( i < 10) {"
return browser
.useXpath()
.click('//*[#id="' + randomCompetitionReference() + '"]/div/div[1]')
.useCss()
.perform(()=>{ // perform() makes sure, that one function call is only executed after the other has fineshed (not concorrent)
return doClick(times -1)
})
} else {
return browser
}
}
doClick(recursions)
}
}
In your case you would call the function 'select a competition' with 10 as the parameter "recursions".
Note that I have changed "randomCompetitionReference" to be a function, so this generates a different value every time it is called. Else it would get one random value when its defined, and would reuse that same value for every click().

Related

How to push several function with args into an array

Hello everyone,
In a Node.js module (a custom queue) i am working on,
I am using the queue module, and I need to push into its array "jobs" several functions with an argument (an int for the estimated time)
When I start the queue, an error that says that the job is not a function.
I think I understand why, it's because the function "process" is executed when I push it. But I need to execute this process later with arguments.
my code :
module.exports = Queue
var process = require("../test/processjob")
var q = require("queue");
function Queue(options) {
if (!(this instanceof Queue)) {
return new Queue(options);
}
options = options || {}
this.queue = q();
/*
handling other options
*/
}
Queue.prototype.processJob = function() {
for (var i = 0, len = this.tasks.length; i < len; i++) {
this.queue.push(process.process(this.tasks[i].estimated_time));// <== push here
}
this.queue.start(); //<== exception here
}
Thanks a lot, and sorry for my poor english.
To push a function into an array, that you'll want to execute at a later point in time, you could wrap that function with another function, i.e.:
this.queue.push(
function(cb) {
process.process(this.tasks[i].estimated_time)
cb();
}
);// <== push here
Or using ES6
this.queue.push((cb) => {
process.process(this.tasks[i].estimated_time);
cb();
});

Angular to control printing , no. of times?

I do have a print Service developed using angular js,
Is there any efficient way that I can mention the no.of times.
Currently I can loop through this code and call n number of times
PrintService.printElement("printThisElement");
Print service code
function printElement(elem) {
var printSection = document.getElementById('printSection');
// if there is no printing section, create one
if (!printSection) {
printSection = document.createElement('div');
printSection.id = 'printSection';
document.body.appendChild(printSection);
}
var elemToPrint = document.getElementById(elem);
//clones the element you want to print
var domClone = elemToPrint.cloneNode(true);
printSection.innerHTML = '';
printSection.appendChild(elemToPrint);
$timeout(function(){
window.print();
}, 0);
window.onafterprint = function() {
printSection.innerHTML = '';
}
};
Can I loop thru this ?
$timeout(function(){
window.print();
}, 0);
Not sure of your question but from what I understand, your PrintService can be called from various controllers within your app. And you want to control the number of times this method can be called.
You can use a global variable, and check its value inside the PrintService.printElement("printThisElement"); method. If the value exceeds your limit, return an error.
var myApp = angular.module('myApp', []);
myApp.value('test', 0);
myApp.factory('PrintService ', ['test', function (test) {
this.printElement = function () {
//check 'test' value and then run print code
//test++ if printing
}
}]);
$timeout does not loops the function, $timeout used to execute the code inside it only once after a time out.
Instead use a variable to loop using if condition.
HEre is an example:
var count = 0;
function myFunction() {
count++;
if(count > 5)
{
return; // you can either return or execute any other code.
}
else
{
window.print();
}
}

How to execute promises "sync" and not in async way

I calling getBubblesUserAccess that returns json objects that are orderd in a special way. This results i wanna run a foreach and get other messages but there i wanna return them in "order". I know that it will run these async but there must be a way that i can force it to "sequential" execution. (above code is my last attempt to add a defer...)
Example
pseudo code - get my groups
{
"id":"016cd1fc-89a3-4e4a-9e6e-a102df1b03d9",
"parent":"53750396-7d26-41f3-913d-1b93276b9e09",
"name":"XX",
"createdBy":"c9c63080-2c5b-4e8e-a093-2cfcd628a9d0",
"hasWriteAccess":true,
"hasCreateAccess":false,
"hasDeleteAccess":false,
"hasAdminAccess":false,
"settingsBubbleId":"00000000-0000-0000-0000-000000000000"
},
{
"id":"016cd1fc-89a3-4e4a-9e6e-a102df1b03d9",
"parent":"53750396-7d26-41f3-913d-1b93276b9e09",
"name":"XX",
"createdBy":"c9c63080-2c5b-4e8e-a093-2cfcd628a9d0",
"hasWriteAccess":true,
"hasCreateAccess":false,
"hasDeleteAccess":false,
"hasAdminAccess":false,
"settingsBubbleId":"00000000-0000-0000-0000-000000000000"
}
From this result i wanna iterate over those parent id strings and call another service that respond with this.
pseudo code
for each group above call another service with parent id and get result. This result will be added to a new JSON object.
"messages":[
{
"id":"f1d1aeda-d4e2-4563-85d5-d954c335b31c",
"text":"asd",
"sent":"2015-09-10T22:31:09.897+00:00",
"sender":"6b9e404b-ef37-4d07-9267-3e7b2579003b",
"senderName":"XXX XXXX"
},
{
"id":"a7ac0432-e945-440e-91ce-185170cbf3de",
"text":"asd",
"sent":"2015-09-10T22:28:24.383+00:00",
"sender":"c9c63080-2c5b-4e8e-a093-2cfcd628a9d0",
"senderName":"ZZZZZ ZZZZ"
},
My problem is that my second foreach are running async (as it should) and i want it to resolve back in same order as first json object...
My code::
var loadBubblesAccess = function () {
if (vm.running && angular.isDefined(vm.running)) { return; }
vm.running = true;
vm.bubblesWithMessages = null;
return BubbleFactory.getBubblesUserAccess().then(function (bubblesAccessTo) {
return bubblesAccessTo;
});
},
loadSubBubbles = function (bubblesAccessTo) {
/**
* Result from chain method with all bubbles user has access to.
*/
var promiseArray = [];
//var promiseArrayError = [];
var i = 0;
/**
* Creates a defer object so that we will not resolve before for each loop has been gone thru.. async problems.
*/
var deferred = $q.defer();
angular.forEach(bubblesAccessTo, function (bubble) {
$log.error(JSON.stringify(bubblesAccessTo));
/**
* Get 20 because default thats default and cache and e-tags are done to that number..
*/
BubbleFactory.getBubbleMessages(bubble.id, 0, 20, false).then(function (data) {
i++;
if (data.messages.length > 0) {
promiseArray.push({ bubbleSortOrder: i, bubbleId: bubble.parent, bubbleName: bubble.name, bubbleMessagesId: bubble.id, bubbleMessages: smartTrim(data.messages[0].text, 400, ' ', ' ...'), bubbleMessagesSent: data.messages[0].sent });
}
else {
// console.log("YYYY::: " + bubble.parent);
promiseArray.push({ bubbleSortOrder:i, bubbleId: bubble.parent, bubbleName: bubble.name, bubbleMessagesId: bubble.id, bubbleMessages: 'Inget meddelande än..', bubbleMessagesSent: '' });
}
});
/**
* Check if we have gone thru all bubbles - when finished we resolve defer object.
*/
if(i===bubblesAccessTo.length)
{
deferred.resolve(promiseArray);
}
});
//$log.debug.log(promiseArray);
vm.bubblesWithMessages = promiseArray;
promiseArray.length = 0;
vm.running = false;
};
loadBubblesAccess().then(loadSubBubbles);
The $q service in AngularJS is described as "lightweight" because it only implements the functions 90% of people need. That keeps its code size small - at the expense of not being able to address requests like yours very easily.
If you have the option, try an alternative such as bluebird. Bluebird provides a reduce() function that can execute an array of promises serially, and return their results in the order they were requested. It makes this task straightforward because your result array will match your data array and you can match up the results very easily.
If you do NOT have that option, there is a standard (if not-exactly-simple) technique with promises where you build an array of the elements you want to promise, then call the processing function (that returns a Promise) on the first value (popped from the array). In the .finally() handler, call the processing function recursively with the next value until it is empty (or an error occurs).
Pseudo-code for this:
var valuesToProcess = [1, 2, 3],
results = [];
function processValue(val) {
myProcessingFunction(val).then(function(result) {
results.push(result);
}).catch(function(e) {
console.log('FAIL!', e);
}).finally(function() {
if (valuesToProcess.length > 0) {
processValue(valuesToProcess.shift());
} else {
// All done - do something with results here
}
});
}
// Note: No error checking done, assumes we have work to do...
processValue(valuesToProcess.shift());
You'll need to adapt this to your use-case but it's a simple technique that guarantees serial operation and result-handling.

Cannot get iteration count of for loop cycle inside of the service result -> then

i have following method:
var i;
$scope.playAllSelectedSounds = function() {
try {
for( i; i < $scope.selectedSounds.length; i++) {
var fileName = $scope.selectedSounds[i].file;
var volume = $scope.selectedSounds[i].defaultVolume;
var filePath = "sounds/" +fileName+".mp3";
console.log(fileName);
MediaSrv.loadMedia(filePath).then(function(media){
console.log(media);
// !!!!!!!!!!! HERE I CANNOT GET value of the i VARIABLE
$scope.selectedSounds[i].state = 1;
// !!!!!!!!!!! HERE I CANNOT GET value of the i VARIABLE
$scope.selectedSounds[i].mediaInstance = media;
media.play();
media.setVolume(volume);
});
}
} catch(e) {
alert(JSON.stringify(e));
console.log(e);
$scope.showAlert("Error", "Error during the playing item");
}
};
Problem is that inside of the service:
MediaSrv.loadMedia(filePath).then(function(media){
I cannot get number o for cycle loop which i need to set in:
$scope.selectedSounds[i].state = 1;
Variable i is global i still cannot reach them. How can i solve it please?
It is not because i is not accessible, it is because i has run out of its limit because loadMedia is async and the value of i within the callback would become $scope.selectedSounds.length, since the for loop would have run out before the callback is invoked.
You could resolve this by using a closure variable representing the current item: You could just make use angular.forEach itself, and you don't event need to worry about accessing the right index. Instead just modify the object itself which is available as 1st argument of forEach evaluator function.
angular.forEach($scope.selectedSounds, function loadMedia(selectedSound, idx){
var fileName = selectedSound.file;
var volume = selectedSound.defaultVolume;
var filePath = "sounds/" +fileName+".mp3";
MediaSrv.loadMedia(filePath).then(function(media){
selectedSound.state = 1;
selectedSound.mediaInstance = media;
media.play();
media.setVolume(volume);
});
});
Also you forgot to initialize i in your case, which will cause your loop to not run at all.
Does it work if you create a local variable var that=i; just before your call to the promise, and then try to get "that" inside the promise return?
Otherwise try this :
for (var i in superarray){
(function(j) {
MyService.get(superarray[j].externalID).then(function(r) {
console.debug(j);
});
})(i);
}

How to extend returned objects in the list returned by $asArray?

I'm having trouble decorate the objects in my list returned by $asArray in angularfire with a new method (not decorating the array itself).
The angularfire documentation seems to suggest that the right way to do this is to override the $$added method in the factory for $FirebaseArray, returning a new object that either encapsulates or extends the snapshot that gets passed in to that method. From the documentation:
// an object to return in our JokeFactory
app.factory("Joke", function($firebaseUtils) {
function Joke(snapshot) {
this.$id = snapshot.name();
this.update(snapshot);
}
Joke.prototype = {
update: function(snapshot) {
// apply changes to this.data instead of directly on `this`
this.data = snapshot.val();
},
makeJoke: function() {
alert("Why did the " + this.animal + " cross the " + this.obstacle + "?");
},
toJSON: function() {
// since we didn't store our data directly on `this`, we need to return
// it in parsed format. We can use the util function to remove $ variables
// and get it ready to ship
return $firebaseUtils.toJSON(this.data);
}
};
return Joke;
});
app.factory("JokeFactory", function($FirebaseArray, Joke) {
return $FirebaseArray.$extendFactory({
// change the added behavior to return Joke objects
$$added: function(snap) {
return new Joke(snap);
},
// override the update behavior to call Joke.update()
$$updated: function(snap) {
this.$getRecord(snap.name()).update(snap);
}
});
});
However, when I do this in my code, nothing ever gets added to the array, although I can see from outputting to the console that it is getting called.
var printMessageObjConstructor = function(snap) {
this.$id = snap.name();
this.snapshot = snap;
this.$update = function(snap) {
this.snapshot = snap;
};
this.printMessage = function() {
return this.author + "'s question is: " + this.body;
};
};
var ref = new Firebase("https://danculley-test.firebaseio.com/questions");
//What Am I Doing Wrong Here?
var arrayFactory = $FirebaseArray.$extendFactory({
$$added: function(snap, prevChild) {
var x = new printMessageObjConstructor(snap);
console.log("I am being called from FirebaseDecoratedCtlOverloadAddedinNewObj.");
return x;
},
$createObject: function(snap) {
return new printMessageObjConstructor(snap);
},
$$updated: function(snap) {
var i = this.$indexFor(snap.name());
var q = this.$list[i];
q.$update(snap);
}
});
var sync = $firebase(ref, {arrayFactory:arrayFactory});
var list = sync.$asArray();
list.$loaded(function(list) {
$scope.questions = list;
});
I've set up a new plunk stripped down to show the issue with a couple other use cases that I've tried. (The actual method I'm adding is more complex and isn't related to the view, but I wanted to do something simple to reproduce the issue.)
I think the issue is that I don't quite understand what exactly $$added is supposed to return, or what additional behavior beside returning the value to be stored $$added is supposed to have. There also doesn't really seem to be an $$added on the prototype or on $FirebaseArray to call as a super to get the default behavior. Can someone point me in the right direction?
UPDATE
For the benefit of others, after reviewing the like that Kato posted, I was able to solve the issue by adding the following, almost all copied directly from the source except for the commented line below.
$$added: function(snap, prevChild) {
var i = this.$indexFor(snap.name());
if( i === -1 ) {
var rec = snap.val();
if( !angular.isObject(rec) ) {
rec = { $value: rec };
}
rec.$id = snap.name();
rec.$priority = snap.getPriority();
$firebaseUtils.applyDefaults(rec, this.$$defaults);
//This is the line that I added to what I copied from the source
angular.extend(rec, printMessageObj);
this._process('child_added', rec, prevChild);
}
}
For the benefit of others, after reviewing the link that Kato posted, I was able to solve the issue by adding the following, almost all copied directly from the source except for the commented line below.
$$added: function(snap, prevChild) {
var i = this.$indexFor(snap.name());
if( i === -1 ) {
var rec = snap.val();
if( !angular.isObject(rec) ) {
rec = { $value: rec };
}
rec.$id = snap.name();
rec.$priority = snap.getPriority();
$firebaseUtils.applyDefaults(rec, this.$$defaults);
//This is the line that I added to what I copied from the source
angular.extend(rec, printMessageObj);
this._process('child_added', rec, prevChild);
}
}

Resources