protractor and for loops - javascript

I have a question concerning below code for test in Protractor.
Namely as you can see firstly I find list of labels and then I check its number(three). Then I have a first loop where I compare each label with value from my table. Here I use i <table.length and then it works correctly.
In the second loop I use labels.count() which is equal to three because i checked it earlier but it doesnt work at all. Protractor goes through this loop no matter what the output of the check is and the test finishes as PASSED.
Can anyone tell me why i <table.length condition in the loop works and i<labels.count doesn't?
//labels list
var labels = element.all(by.xpath("//form[#name='form']//label"));
//test start
describe('angularjs homepage', function() {
it('test1', function() {
browser.get('http://www.way2automation.com/angularjs-protractor/registeration/#/login');
//shows 3
labels.count().then(function(text){
console.log( text);
});
var table = ["Usern1ame","Password","Username *"];
//first loop -> this one works if there is a difference between 'table' and element from 'label' list
for (var i = 0; i <table.length; i++) {
expect(labels.get(i).getText()).toEqual(table[i]);
}
//this one doesn't -> if there is a difference between 'table' and 'label'
//list nothing happens, no errors, test passes
for (var i = 0; i <labels.count(); i++) {
expect(labels.get(i).getText()).toEqual(table[i]);
}
});
});

In your example, labels.count() is a promise and you cannot use it directly. To get the value of count, you need to resolve the promise first. Look at below code,
labels.count().then(function(labelCount){
for (var i = 0; i <labelCount; i++) {
expect(labels.get(i).getText()).toEqual(table[i]);
}
})

Related

My js file keeps on repeating my the previous list item. for example if i add a text on my form, the previoius list item repeat itself with new one

let leadTracker = [];
const inputText = document.getElementById("input-text");
const inputButton = document.getElementById("input-button");
const Listing = document.getElementById("listings");
inputButton.addEventListener("click", function () {
leadTracker.push(inputText.value);
clicker();
});
function clicker() {
for (let i = 0; i < leadTracker.length; i++) {
Listing.innerText += leadTracker[i];
console.log(leadTracker);
}
}
I added bro to the array and then back but bro is repeated. This is exactly the same code I read online but it is still not working fine for me.
Each time you call clicker it's going to add the whole array to Listing, including stuff that's already there.
Did you mean to clear out Listing at the beginning of clicker, before the for loop?
Listing.innerText = ""
You have to ways to do this :
first you have to not use for loop in the function
function clicker() {
Listing.innerText += leadTracker[leadTracker.length-1];
console.log(leadTracker);
}
or you can remove all innerText from the element then re-write it again :
function clicker() {
Listing.innerText = "";
for (let i = 0; i < leadTracker.length; i++) {
Listing.innerText += leadTracker[i];
console.log(leadTracker);
}
}
Each time you click the button you take the content of the text field and add it to the leadTracker array. So the array grows with each click, longer and longer. So far so good.
But in the the clicker function, which also runs on every click, you take the entire content of the array and append it to Listing. They array is not cleared in between clicks, so old items in it will be printed again.
You can either skip using an array at all:
inputButton.addEventListener("click", function () {
Listing.innerText += inputText.value;
});
or replace the inner text of Listing instead of appending it, as suggested in other answers.

Is there an alternative to console.log() which lets you print out and update a single line instead of spamming the console?

I am looking for a way to print out a given variable f.ex. the i of a for-loop for each iteration, without it resulting in the entire console being filled with new lines of new values. Is there a console. method for just printing one line, and updating that as we go?
I realise that you could do this by implementing a text-field in your program which you change the with each iteration, but if there is a way of doing this in the console it would be a bit easier (and perhaps quicker? although I am really not sure about that). Thanks in advance.
If there is still confusion about what im asking, what i want is my console to print out:
"i = " i once, and then update the i in that one line, instead of:
i=1
i=2
i=3
1=4
.
.
.
which gets really messy as you go. For the exact example of the i in a for loop, you could get this value from just console.log()'ing the same thing for each iteration, and a number will pop up beside it (in firefox anyway), but i would like to be able to do this with some more useful information.
Option 1: use console.groupCollapsed() and console.groupEnd():
console.groupCollapsed();
for (let i = 0; i < 100; i+= 1) { console.log(`i = ${i}`) }
console.groupEnd();
Option 2: set the values in an array or a string and log the var when the iterations finish:
let valuesToLog = [];
for (let i = 0; i < 100; i+= 1) { valuesToLog.push(`i = ${i}`) }
// see it as an array
console.log(valuesToLog);
// see it as a string, with each value separated by ', '
console.log(valuesToLog.join(', '));
how about JQuery console.clear()?
$(function(){
loop_counter();
});
function loop_counter() {
for (let i = 1; i <= 100; i++){
console.clear();
console.log ("i=", i)
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Ng-Options Expression Repeatedly Called

I am having multiple issues with my <select> element in angular and am trying to understand what is going on. My first step is to understand why the multiple console.log() messages I have put in for debugging repeatedly appear in the console, as in, instead of the message appearing once like I would expect, they appear an infinite number of times, as if part of an infinite loop. Is this how a function called from an ng-options is supposed to behave? If so, I don't understand why, if not, then I would like to fix my loop.
My html: <select ng-options="theorderdate as theorderdate for theorderdate in getOtherOrderDates(Bread.text)" ng-model="randomDateRomantic.randomDateRomantic" ng-change="soRomantic(Bread.text)"></select>
The console.log() messages appear from the getOtherOrderDates() function, which is below (with my comments included):
$scope.getOtherOrderDates = function(loaf) {
var istheLoafdaily = false;
var theorderdates = [];
for (var i = 0; i < $scope.theBreadsList.length; i++) {
for (var z = 0; z < $scope.theBreadsList[i].breads.length; z++) {
if ($scope.theBreadsList[i].breads[z].text == loaf && i > 0) //not a daily loaf, goes beyond "Daily Breads"
{
console.log(theorderdates);
theorderdates = theorderdates.concat($scope.theBreadsList[i].breads[z].orderDates); //concat the matched bread's order dates
console.log(theorderdates, $scope.theBreadsList[i].breads[z].orderDates);
theorderdates = _.sortBy(theorderdates, function(m) {
return m.getTime()
});
for (var y = 0; y < theorderdates.length; y++) {
theorderdates[y] = theorderdates[y].toLocaleDateString();
}
theorderdates = _.uniq(theorderdates);
if (theorderdates.length > 0) {
console.log("Something is wrong here"); //problem
$scope.randomDateRomantic.randomDateRomantic = theorderdates[0];
}
console.log(theorderdates);
return theorderdates;
} else if ($scope.theBreadsList[i].breads[z].text == loaf && i == 0) { //a daily loaf, i == 0
console.log("The bread matched is daily", loaf); //***
istheLoafdaily = true;
console.log(theorderdates); //***
theorderdates = theorderdates.concat($scope.theBreadsList[i].breads[z].orderDates); // concat the matched bread's order dates
console.log(theorderdates, $scope.theBreadsList[i].breads[z].orderDates); //***
break; // escape the for loop, should it be two breaks?????? yes...
} else if (istheLoafdaily && i > 0 && $scope.theBreadsList[i].breads[z].orderDates.length > 0) { //not sure what scenario this matches, hence wtf
theorderdates = theorderdates.concat($scope.theBreadsList[i].breads[z].orderDates);
console.log("wtf");
}
}
}
//end of outermost for loop
//not sure what this is doing because this functionality is repeated up there^ (for non-daily breads)
theorderdates = _.sortBy(theorderdates, function(m) {
return m.getTime()
});
for (var y = 0; y < theorderdates.length; y++) {
theorderdates[y] = theorderdates[y].toLocaleDateString();
}
theorderdates = _.uniq(theorderdates);
if (theorderdates.length > 0) {
$scope.randomDateRomantic.randomDateRomantic = theorderdates[0];
console.log("Something is wrong here (daily)"); //problem
}
return theorderdates;
//not sure what this is doing because this functionality is repeated up there^ (for non-daily breads)
//if change to Locale date string then not unique, but if don't change then not a date to sort!!!!!!! >:(
},
I am getting almost all console messages an infinite number of times, without doing anything such as firing the ng-change function. I just add a daily bread to my cart for instance, and then the console gets filled with the following messages, that I have starred in my code.
My theBreadsList is not very long, so there is something going on that it is going repeatedly like this. Even if I broke out of the for loop twice as you will see in my code, it wouldn't explain the fact that it logs to the console all the time, because eventually the loop would not be satisfied, and this wouldn't take to long as has been mentioned.
Please advise, thank you. If you need more information, I am happy to provide.
The getOtherOrderDates will be called in each digest cycle so that angular knows whether to update options in select. That's most likely the reason you're seeing this method being called many times.
If you're worried about performance impact of this loop you can build the options upfront inside your controller store it in $scope like so:
$scope.options = $scope.getOtherOrderDates($scope.Bread.text);
whenever $scope.Bread.text changes and then use $scope.options inside your template.
To avoid triggering your loops in every digest loop you can use one time binding ::value.
<select ng-options="theorderdate as theorderdate for theorderdate in ::getOtherOrderDates(Bread.text)"
ng-model="randomDateRomantic.randomDateRomantic"
ng-change="soRomantic(Bread.text)"></select>
Thanks to that expression inside ng-options will be evaluated only once and the watcher will be removed after first evaluation which will stop your function being triggered in next digest loop iterations.
DOCS

Simplifying a javascript function with repeated similar lines (with a loop?)

Okay, I hope you don't all facepalm when you see this - I'm still finding my way around javascript.
I am putting together an RSVP form for a wedding website.
I want the guests to be able to add their names to the RSVP form, but only have as many fields showing as required. To this end, after each name field, there is a link to click, which will, when clicked, show a name field for the next guest.
The code below works... but I am sure it can be tidier.
I have tried to insert a for() loop into the code in several different ways, I can see that the for() loop increments correctly to the last value - but when it does so, it leaves only the last addEventListener in place. I can only assume, that I should be using a different kind of loop - or a different approach entirely.
How should I tidy up the following?
<script>
function showNextGuest(i) {
document.getElementsByTagName(\'fieldset\')[i].style.display = \'block\';
}
function initiateShowNextGuest() {
document.getElementsByTagName('fieldset')[0].getElementsByTagName('a')[0].addEventListener('click',function(){showNextGuest(1);},false);
document.getElementsByTagName('fieldset')[1].getElementsByTagName('a')[0].addEventListener('click',function(){showNextGuest(2);},false);
document.getElementsByTagName('fieldset')[2].getElementsByTagName('a')[0].addEventListener('click',function(){showNextGuest(3);},false);
document.getElementsByTagName('fieldset')[3].getElementsByTagName('a')[0].addEventListener('click',function(){showNextGuest(4);},false);
document.getElementsByTagName('fieldset')[4].getElementsByTagName('a')[0].addEventListener('click',function(){showNextGuest(5);},false);
}
window.onload = initiateShowNextGuest();
</script>
Your intuition is right - a for loop could indeed simplify it and so could a query selector:
var fieldsSet = document.querySelectorAll("fieldset"); // get all the field sets
var fieldss = [].slice.call(asSet); // convert the html selection to a JS array.
fields.map(function(field){
return field.querySelector("a"); // get the first link for the field
}).forEach(function(link, i){
// bind the event with the right index.
link.addEventListener("click", showNextGuest.bind(null, i+1), false);
});
This can be shortened to:
var links = document.querySelectorAll("fieldset a:first-of-type");
[].forEach.call(links, function(link, i){
link.addEventListener("click", showNextGuest.bind(null, i+1), false);
});
function nextGuest () {
for(var i = 0; i < 5; i++){
document.getElementsByTagName('fieldset')[i]
.getElementsByTagName('a')[0]
.addEventListener('click',function(){
showNextGuest(parseInt(i + 1));
}, false);
}
}
Benjamin's answer above is the best given, so I have accepted it.
Nevertheless, for the sake of completeness, I wanted to show the (simpler, if less elegant) solution I used in the end, so that future readers can compare and contrast between the code in the question and the code below:
<script>
var initiateShowNextGuest = [];
function showNextGuest(j) {
document.getElementsByTagName('fieldset')[j].style.display = 'block';
}
function initiateShowNextGuestFunction(i) {
return function() {
var j = i + 1;
document.getElementsByTagName('fieldset')[i].getElementsByTagName('a')[0].addEventListener('click',function(){showNextGuest(j);},false);
};
}
function initiateShowNextGuests() {
for (var i = 0; i < 5; i++) {
initiateShowNextGuest[i] = initiateShowNextGuestFunction(i);
initiateShowNextGuest[i]();
}
}
window.onload = initiateShowNextGuests();
</script>
In summary, the function initiateShowNextGuests() loops through (and then executes) initiateShowNextGuestFunction(i) 5 times, setting up the 5 anonymous functions which are manually written out in the code in the original question, while avoiding the closure-loop problem.

Getting th ChildNodes from an object variable

I've ran into a small snag with my code - below is my code:
var actionsAllowed = $(packet).find('actionsAllowed');
This returns to me the following in the firebug console:
Object[actionsAllowed]
Clicking "actionsAllowed" takes me into the packet and to the correct section, where I see the two listed actions.
I can expand the object and eventually see the following:
Object[actions]
0
actions
remove
remove()
attributes
[]
baseURI
"http://localhost:9000/testget#"
childElementCount
2
childNodes
NodeList[ActionOne, ActionTwo]
0
ActionOne
1
ActionTwo
length
2
item
item()
iterator
iterator()
__proto__
NodeListPrototype { item=item(), iterator=iterator()}
So under the NodeList I see the correct actions.
The issue I am having is that I don't know how to get those actions out of there and listed or even just have them available as separate variables.
My attempt at getting then logging each child:
function getActionsAllowed() {
var children = actionsAllowed.childNodes;
for (var i = 0; i < children.length; i++) {
console.log(children);
}
}
Problem is, ".childNodes" keeps returning as "undefined".
Is there another, better way to do this? Or is this correct but I've made a mistake?
Thank you.
Kind Regards,
Gary Shergill
EDIT:
working code for just one result:
var currentState = $(packet).find('currentState').text();
var actionsBanned = $(packet).find('actionsBanned').text();
EDIT 2:
Updated code to:
$(packet).find('actionsAllowed').each(function () {
var children = this.childNodes;
for (var i = 0; i < children.length; i++) {
var action = children[i].nodeName
console.log(action);
}
});
This works =) It logs each action one by one, so it's working. Just a matter of choosing how to change the console.log() to something more useful (need to define each one seperately...).
Will create a new thread if I have trouble and link it from here.
(my related thread: Returning Arrays and ChildNodes)
You can always extract the DOM nodes from the jQuery object using toArray or get.
var actionsAllowed = $(packet).find('actionsAllowed').get();
But that's generally not necessary since the jQuery object itself implements an extensive API to manipulate the nodes.
E.g. looping over the nodes
$(packet).find('actionsAllowed').each(function () {
//looping over childnodes
$(this).children().each(function () {
console.log($(this).text());
});
});
If you just want to children of actionsAllowed nodes directly, you can also do:
$(packet).find('actionsAllowed > *').each(function () {
console.log($(this).text());
});

Categories

Resources