Cypress: How to add loop based on the array length? - arrays

I have this code where i get 'ul[class="menu"] > li' get there text and convert it to arrays 'list'
it('List to array', () => {
const list = []
cy.get('ul[class="menu"] > li').children()
.each(($ele) => {
list.push($ele.text().toString().trim())
})
.then(() => {
cy.get('ul[class="menu"] > li').should('contain',list[0])
cy.log(list[0])
})
})
Now i want to add a loop until it finished my array in list that will assert (should) based on my last code because the length of list is constantly changing and i don't want a repeated code
How can i achieved this?

Take a look at docs for each(), Cypress passes in each element and also it's index.
Use the index to get the value from the list.
cy.get('ul.menu > li') // same as 'ul[class="menu"] > li'
.each(($el, index) => {
expect($el.text()).to.eq(list[index])
})

Related

Cypress.io Iterate list of elements to see if text does not exist, then perform action to add element

I'm trying to iterate over a list (<UL>) to see whether or not a specific string exists within the <li> list item elements. If the string (Lemon in this example) does not exist then I want to perform an action to add the element. Otherwise, if the string does exist then I just want to do nothing and continue on.
Sample html:
<ul id=theTree>
<li>Apple</li>
<li>Pear</li>
<li>Orange</li>
</ul>
I have tried:
let found = false
cy.get('#theTree').find('li').each( ($node, i, $list) => {
if ($node.textContent.includes("Lemon")) {
found = true
}
})
// At this point after each <li> has been iterated over, will 'found' be true or false?
if (!found) {
// Add "Lemon" to the list here if it was not found in the <ul> list...
} else {
// Do nothing. Lemon already exists in the list.
}
The if condition "$node.textContent" is returning undefined and not returning the text content from the list elements. Therefore, the .includes() function throws an error.
Also, I have feeling the asynchronous callback function from .each() needs a Promise or some other means of saving the "found = true" state, such that, the second if block can correctly determine whether or not "Lemon" was found in the list iteration.
Any help is very much appreciated.
Perhaps iterating with .each() is not the easiest way to do this.
Using .then() allows you to pass the found value down the chain. You could also add an alias if you want to use it later in the test.
Setting a variable external to the command chain is called a "backflip" and can cause problems depending on the test structure.
cy.get('#theTree').find('li')
.then($els => {
const texts = [...$els].map(el => el.innerText) // extract texts
const found = texts.includes('Lemon')
return found
})
.then(found => {
if (!found) {
...
})
With an alias
cy.get('#theTree').find('li')
.then($els => {
const texts = [...$els].map(el => el.innerText) // extract texts
const found = texts.includes('Lemon')
return found
})
.as('found')
// later
cy.get('#found').then(found => {
if (!found) {
...
})
You have to check the result after the each command will be executed.
For instance, you can use then command as follows:
let found = false
cy.get('#theTree').find('li').each( ($node, i, $list) => {
if ($node.textContent.includes("Lemon")) {
found = true
}
}).then(() => {
// this callback will be executed after 'each' command so we know the result at this point.
if (!found) {
// Add "Lemon" to the list here if it was not found in the <ul> list...
} else {
// Do nothing. Lemon already exists in the list.
}
})

How to find a user in different collections in Firestore using ReactJS?

I have two collections: users_unprocessed and users_processed. When a user is new, he will be added to the users_unprocessed collection. If he is processed, he will be deleted and added to the users_processed.
I want to create a list with all users. Therefore I need to find a user in users_processed or users_unprocessed. The list should be reactively and show live updates, therefor I need to use .onSnapshot().
database.collection("users_unprocessed").doc(id).onSnapshot((snapshot) => {
if (snapshot.numChildren > 0) {
setFetchedUser(snapshot.data());
} else {
database.collection('users_unprocessed').doc(id).onSnapshot((snap) => {
if (snapshot.numChildren > 0) {
assignUser(snap)
} else {
// Error Handling
}
})
}
This code is not giving my any result no matter of the doc exists in the users_unprocessed or users_processed.
If both collections are correctly set you just need to use the forEach function on each of them and put them on an array or list or whatever you want. Something like that:
const allUsers = [];
database.collection("users_unprocessed").onSnapshot((querySnapshot) => {
querySnapshot.forEach((doc) => {
allUsers.push(doc.data().name);
});
});
And then you can do the same with the other collection in the same list. If you don't want to put them on an Array but you have any other method or function you just need to change the last part.

React Native: update an Array inside an Object

i currently have an object and inside this object i have multiple objects and Arrays. I want replace an Array inside this object with a new Array, so i thought of making a copy of the entire object and simple replace the Array i wan to change with the updated Array. My problem is i couldnt complete my code, i have the idea of how to do it but cant execute it.
setListings(listings=>
listings.map(item =>{
if(item.id === msg.id){
//console.log(item)
//console.log(item.Message)
const newMessages = [msg,...item.Messages]
//console.log(newMessages)
return console.log([msg,...item.Messages],{...item}) // just for testing purpose i
am returning a console log
to see what it will get me. Not correct.
}
return item;
})
);
So basically listings is my state variable, here console.log(item) prints out the entire object, console.log(item.Messages) prints out the current Messages Array which i want to replace, console.log(newMessages) prints out the new Messages Array which i want to replace the current Messages array with.
cartItem.map((food,index)=> {
if(food.food_id == newFoodItem.food_id && food.id == newFoodItem.id){
const AllFoodData = cartItem
AllFoodData[index] = newFoodItem
AsyncStorage.setItem('#Add_cart_Item', JSON.stringify(AllFoodData))
.then(() => {})
.catch(err => console.log(err))
ToastAndroid.showWithGravityAndOffset('Cart Replace Successfully',ToastAndroid.LONG,ToastAndroid.BOTTOM,25,50 )
}
})
So basically what i want to achieve here is to add the msg object to the existing Messages Array.
Since lsitings is an Array of objects using the .map i can spread through each object and check if the id of that object is each to my msg.id. if that is true then i want to return a copy the that specific listing and edit the Messages Array within [msg, ...item.Messages] otherwise return the existing item.
setListings(listings=> listings.map(item => {
if(item.id === msg.id) {
return {
...item,
Messages: [msg, ...item.Messages]
}
}
return item;
}));
});

How to sort how you want the the data from firestore

I have a firestore with lessons, see the picture below, I get from the firestore the title property from each object and display it in the browser, but everytime I refresh the website the lessons are sorted by how they want, why is that happening? I want to sort them how I want, I want to start with 'Introduction' and so on, how can I do that? I think the orderBy() is not working here.
As you see in the image above, the order in the firestore is alphabetical, but in my page is sorted by its own, see the picture below.
I want the result to be by in a specific order, for example we have the following titles, these titles are from the firestore: "Display", "Introduction", "Alignment", my problem is that these 3 titles are in a new order every time I refresh the website, I want them to be: "Introduction", "Alignment", "Display". In my case I have more titles but this is what's happening, I don't know how to align them how I want or even alphabetical if is possible.
Below is the code that I used to get the data from firestore:
useEffect(() => {
db.collection("users")
.doc(`${user.uid}`)
.get()
.then((doc) => {
const allData = { ...doc.data(), id: doc.id };
const intoArray = Object.entries(allData);
intoArray.sort(); // I used sort here because I had the same problem
// (every time a new order) with the
// data when I converted it to an array
const getCSSLessons = intoArray[0][1];
const cssData = Object.values(getCSSLessons);
setCss(cssData);
const getHTMLLessons = intoArray[1][1];
const htmlData = Object.values(getHTMLLessons);
setHtml(htmlData);
const getResLessons = intoArray[3][1];
const resData = Object.values(getResLessons);
setRes(resData);
})
.catch((error) => console.log(error));
}, [user]);
I tried using sort(), for a variable (htmlData) but its not working.
Also, I use map() to display them, if this helps you to answer to my question.
If you use sort without any argument, it will sort array elements alphabetically. It looks like your array elements are arrays, which will end with unexpected behaviors. Use sort argument to ensure it uses your own sorting rules. For example:
const intoArray = Object
.entries(allData)
// I don't know what should be the sorting algorithm
// As an example, I consider each element (`a` and `b`) to
// be arrays and compare both first element as a Number
.sort( (a, b) => a[0] - b[0])
Edit
A more secure way to find elements in an array is to use find:
const getCSSLessons = intoArray[0]
.find( element => element.name === 'CSS Lessons');
I was doing something unnecessary as you see in the first picture I had a main object css and in that object I had sub-objects like alignment and in the sub object I had the properties that I want to display, that sub object was unncessary, istead of sub objects with a pre defined name, I let the firebase to count the sub objects and add as a name a number and the order is the same as I wanted to be.
The code that I used to add data to firebase:
fire
.auth()
.createUserWithEmailAndPassword(email, password)
.then((cred) => {
return db
.collection("users")
.doc(cred.user.uid)
.set({
css: [
{
title: "Introduction",
path: "/css3/introduction",
},
{
title: "Priority",
path: "/css3/priority",
},
],
});

Storing an array of elements using Cypress

I want to store an array of strings taken from a page using cypress.
so far I have:
cy.get(".product-name").each(($el) => {
let text = $el.text();
cy.request("POST", "http://localhost:3000/sale-items", {
text
});
cy.wait(1000);
});
As you can see I am having to make separate requests for every item. But I want to store the entire array somehow, and then make one request.
I have read this page on variables and aliases but feel no closer to achieving what I want.
How to store an array of items generated from using Cypress commands?
You can use .then() instead of .each() to retrieve all elements classed 'product-name'. The parameter is an iterable collection that can be converted to an array.
Ref Array.from
cy.get(".product-name").then(($els) => {
const texts = Array.from($els, el => el.innerText);
cy.request("POST", "http://localhost:3000/sale-items", {
texts
});
cy.wait(1000);
});
cypress provide .each($elem, index, $list) method to iterate each
element of the array.
cy.get(".product-name").each(($els, index, $list) => {
// $list itself is collection of element
// $elem is each element of array
// index is postion of element in array
});
function getProductName() {
let countProducts
cy.get('.product-name')
.then((elements) => {
countProducts = elements;
})
return countProducts
}
Cypress.Commands.add('getProductName', getProductName)
To invoke the method from any test:
cy.getProductName().then(element => {
cy.log(`Product names are: ${element.text()} `)
})

Resources