Iterating Array in Cypress - arrays

I am pushing Div element in an array and then iterating each one by one, but when I came out of first each section the array length becomes 0 and i am unable to enter into for loop.
in for loop, i want to click a section in each div and perform an assertation and go back to the previous section.
let Array =[]
cy.get('.tag-wrapper-item').each(($pills) => {
cy.log($pills)
// cy.log('Success')
Array.push($pills)
cy.log(Array.length)
})
cy.log(Array.length)
for (const element of Array) {
cy.wrap(element).click()
cy.wrap(element).find('.--tag.tag-link.--active.tag-selector-button-link').click()
var OneOffModel = element.text()
cy.get('.heading-markdown').contains(OneOffModel)
cy.go('back')
}

A better approach might be to get the count of .tag-wrapper-item and loop on that
cy.get('.tag-wrapper-item')
.its('length')
.then(count => {
for (let index = 0; index < count; index++) {
cy.get('.tag-wrapper-item').eq(index)
.click()
cy.get('.tag-wrapper-item').eq(index)
.find('.--tag.tag-link.--active.tag-selector-button-link')
.click()
cy.get('.tag-wrapper-item').eq(index)
.invoke('text')
.then(OneOffModel => {
cy.get('.heading-markdown').contains(OneOffModel)
})
cy.go('back')
}
})
If you alias each element in the loop, Cypress will try to overcome the detached-from-DOM error when you read the alias
cy.get('.tag-wrapper-item')
.its('length')
.then(count => {
for (let index = 0; index < count; index++) {
cy.get('.tag-wrapper-item').eq(index)
.as('current')
.click()
cy.get('#current')
.find('.--tag.tag-link.--active.tag-selector-button-link')
.click()
cy.get('#current')
.invoke('text')
.then(OneOffModel => {
cy.get('.heading-markdown').contains(OneOffModel)
})
cy.go('back')
}
})

Cypress commands asynchronous, so at the time the Cypress walks through your array, it's not yet been initialized.
To fix this, you can wrap your code with a then callback:
let Array =[]
cy.get('.tag-wrapper-item').each(($pills) => {
cy.log($pills)
// cy.log('Success')
Array.push($pills)
cy.log(Array.length)
})
cy.then(() => {
cy.log(Array.length)
for (const element of Array) {
cy.wrap(element).click()
cy.wrap(element).find('.--tag.tag-link.--active.tag-selector-button-link').click()
var OneOffModel = element.text()
cy.get('.heading-markdown').contains(OneOffModel)
cy.go('back')
}
})

Related

Cypress assert is element inside array

Can someone please, assist in following:
Short explanation: opened one page, taken text element, then opened second page nd among 4 or 5 elements, need to assert that element from page one, is inside created array of those several elements. Wrote this code:
Cypress.Commands.add(
'assertForOpenedElementVisible',
(list1, list2, notDisplayedElementMsg) => {
const textsArray = []
cy.get('body').then((body) => {
if (body.find(list1).length > 0) {
cy.get(list1).each(($el, index) => {
const text1 = $el.text().replace(', ', '')
cy.get(list1).eq(index).click()
cy.wait(1000)
cy.get(list2)
.each(($el, index) => {
const text = $el.text().replace(', ', '')
textsArray.push(text)
cy.log(textsArray)
cy.log(text)
})
.then(() => {
cy.wrap(expect(textsArray).to.include(text1))
})
})
} else {
cy.log(notDisplayedElementMsg)
}
})
}
)
And when check Test runner - I got elements, but test fails:
How to correctly assert that? Thank you in advance
You can do an assertion like:
expect(text1).to.be.oneOf(textsArray)
OR, you can directly assert without using each() as well like:
cy.get(list2).should(($list2) => {
expect($list2.eq(3)).to.contain('49') //If you know the position
})
cy.get(list2)
.invoke('text')
.then((text) => {
expect(text).to.contain('49') //If you don't know the position
})

values inside foreach pushes undefined?

So I am working with a tsx file and I tried to push some html into a variable with options in it that takes their value from foreach item and so far so good but then I try to push them into some array and use it in my state in order to do the right thing for react it pushes undefined. ı am confused how it gives undefined when I try to push it into array and it gives value when I try to push it into html element
provider.getItems().then((items:any[]) => {
items.forEach((item) => {
this.categoryOptItems.push(item.Title);
this.categoryOpts.push(<option value={item.Title}>{item.Title}</option>)
})
this.setState({categories:
this.categoryOptItems
})
})
I found my solution but not sure if it is the best way though.
This is in componentDidMount
provider.getItems().then((items:any[]) => {
items.forEach((item) => {
categoryOptItems.push(item.Title);
})
this.setState({categories:
categoryOptItems
})
})
This is the function
options() {
var categories = this.state.categories;
const html:any = [];
for (var i = 0; i < this.state.categories.length; i++) {
html.push(<option value={i+1}>{categories[i]}</option>);
}
return html;
}

drag to reorder and save state with reactjs

I'm writing a small app that uses the react-sortable-hoc
everything is great but im having issues displaying the list ordered by order
I have
user 0
user 1
user 2
when I drag user 2 above user 0
instead of getting
user 2
user 0
user 1
I get
user 2
user 1
user 0
I think It has to do with the way I'm setting the order in the state. but I can't figure it out.
this is how I set the order on sort end
const onSortEnd = ({ oldIndex, newIndex }) => {
setUsers(prevState => {
const newItems = [...prevState];
newItems[newIndex].order = oldIndex;
newItems[oldIndex].order = newIndex;
return newItems.sort((a, b) => a.order - b.order);
})
};
here's the app running so you can play with it.
https://codesandbox.io/s/winter-https-xelrd?fontsize=14&hidenavigation=1&theme=dark
I have fixed it,
here is the working url to play with https://codesandbox.io/s/quizzical-colden-rm62y
You were correct in guessing that the problem was with the onSortEnd function. Instead of swapping the newIndex and oldIndex position we just need to either bubble them up or down.
Here is a working code, it can be cleaned up a bit, but you got the idea :)
const onSortEnd = ({ oldIndex, newIndex }) => {
setUsers(prevState => {
const newItems = [...prevState];
if (oldIndex > newIndex) {
for (let i = oldIndex - 1; i >= newIndex; i--) {
newItems[i].order++;
newItems[oldIndex].order = newIndex;
}
} else if (oldIndex < newIndex) {
for (let i = oldIndex + 1; i <= newIndex; i++) {
newItems[i].order--;
newItems[oldIndex].order = newIndex;
}
}
return newItems.sort((a, b) => a.order - b.order);
});
};
Hope it helps. Happy coding :)
What you do is swapping.
If you want to just "insert" the element in the new position you will have to update all the items between the two positions.
In your case, one approach would be to just move the element and re-create the order for all items
setUsers(prevState => {
const newItems = [...prevState];
newItems.splice(newIndex, 0, newItems.splice(oldIndex, 1)[0]).forEach((item,index)=>{
item.order = index;
});
return newItems
});
Demo at https://codesandbox.io/s/confident-river-mrh3p
So looks like your code is simply swapping the elements. This does not seem like what you really want to do. In fact you really want to remove the element and insert it at a given position. I think since you already have the oldIndex and newIndex, you can approach the sort function as follows:
const onSortEnd = ({ oldIndex, newIndex }) => {
setUsers(prevState => {
var newItems = [...prevState];
let elem = newItems[oldIndex]
newItems.splice(oldIndex, 1)
newItems.splice(newIndex, 0, elem)
return newItems
});
};
There isn't really a need for order and is capturing more than the minimum state required (unless you use it elsewhere).

Iterating through HTMLcollection in React,js

I am having trouble iterating through and HTMLCollection in a React.js component. I have the following function:
shuffleLists = () => {
var elems = document.getElementsByTagName("ul");
console.log(elems)
for (let item of elems) {
console.log(item);
}
}
console.log(elems) prints out an HTMLCollection of ul elements as expected. But the for loop after it doesn't print anything in the console, when I would expect to see each ul element printed in the console. What am I doing wrong?
Edit for clarity:
The key issue here is that the line console.log(item) inside the loop does not output anything into the console in chrome dev tools, and the same applies to other various loop syntaxes as discussed in the answers and comments below.
I also have noticed that there is different lengths being logged from console.log(elems) between different browsers. In chrome I see HTMLCollection[] length: 10 ...
But in Firefox I see
HTMLCollection {length 0} ...
The item in your for loop is actually the key (array index). You need to get the particular element from the elems array by using the syntax elems[item].
Refactoring your code (and changing item to key, just for clarity):
shuffleLists = () => {
var elems = document.getElementsByTagName("ul");
console.log(elems);
for (let key of elems) {
console.log(elems[key]);
}
}
UPDATE: because result of getElementsByTagName() is a NodeList
shuffleLists = () => {
var elems = document.getElementsByTagName("ul");
console.log(elems);
elems.forEach(function(val) {
console.log(val);
})
}
From the MDN Web Docs
Although NodeList is not an Array, it is possible to iterate over it
with forEach(). It can also be converted to a real Array using
Array.from().
However, some older browsers have not implemented NodeList.forEach()
nor Array.from(). This can be circumvented by using
Array.prototype.forEach().
As per the updated requirement of the OP
shuffleLists = () => {
var ulElems = document.getElementsByTagName("ul");
console.log(ulElems);
for(i = 0; i < ulElems.length; i++) {
var liElems = ulElems[i].getElementsByTagName("li");
for(j = 0; j < liElems.length; j++) {
console.log(liElems[j].innerHTML);
}
}
}
document.getElementsByTagName will return an array of elements, in your case array of elements in the page, so you need to have something like the following if you want to have the first ul
shuffleLists = () => {
var elems = document.getElementsByTagName("ul");
console.log(elems)
for (let idx of elems) {
console.log(elems[idx]);
}
}
I would suggest to use document.getElementById() to be more precise about your selected elements
I figured out my problem was that I was calling the shuffleLists function from the parent component in React, prior to the ul and li elements I wanted to iterate through being rendered in the sub-component. The console output for the elems var was confusing as it showed all the lists and list items inside the HTMLCollection.
When I moved the shuffleLists function to the sub-component and called it inside componentDidMount I was able to loop through and console out all the list items as desired.

How to store data from firebaselistobservable to an array?

I'm trying to copy the data from firebase to an array using angular 2. But i'm unable to push the data into the array.
Here's the code:
Variables:
uid: string = '';
agencyItems: FirebaseListObservable<any[]>;
trackerItems: FirebaseListObservable<any[]>;
agencyID: any[] = [];
getData()
this.af.auth.subscribe(auth => {
if (auth) {
this.uid = auth.auth.uid;
}
});
this.getAgencyData();
console.log("AgentID: ",this.agencyID);
console.log("Array Length = ",this.agencyID.length); //PROBLEM HERE: Array agencyID is still 0.
this.getTrackerData();
getAgencyData():
console.log("Fetching agency data");
this.agencyItems = this.af.database.list('/agencies/',{preserveSnapshot:true});
this.agencyItems.subscribe(snapshots => {
snapshots.forEach(snapshot => {
console.log(snapshot.val()._id);
this.agencyID.push(snapshot.val()._id);
});
});
getTrackerData():
for (let i = 0; i < this.agencyID.length; i++)
{
console.log("Fetching Tracker data");
this.trackerItems = this.af.database.list('/tracker/' + this.agencyID[i]);
this.trackerItems.subscribe(trackerItems => trackerItems.forEach(Titem =>
console.log("Tracker name: " + Titem.name),
));
}
Here is the debug console screenshot:
Since i'm a newbie to web programming some code may seem completely unnecessary.
What am I doing wrong in this code? How can I implement the same.
The problem is the location where, or better WHEN, you are checking the length of the array. You make an asynchronous call when you fetch the data, but you are checking the length of the array before the data has been returned. Therefore the array is still empty.
Try the following in getAgencyData():
console.log("Fetching agency data");
this.agencyItems = this.af.database.list('/agencies/',{preserveSnapshot:true});
this.agencyItems.subscribe(snapshots => {
snapshots.forEach(snapshot => {
console.log(snapshot.val()._id);
this.agencyID.push(snapshot.val()._id);
console.log("Array Length = ",this.agencyID.length); // See the length of the array growing ;)
});
// EDIT
this.getTrackerData();
});

Resources