How to customize assertions in Cypress - javascript

I need to test that an array of object contains a certain value. The test is written with Cypress, and for that, I use cy.wrap and .some().
My code looks like this:
const myCustomArray = [{ name: 'Lisa' }, { name: 'Katie' }];
cy.wrap(myCustomArray.some((user) => {
if (user.name === 'Lisa') {
return true;
} else {
return false;
}
})).should('eq', true);
This works well, but the problem is that it then returns me a very non-specific message in the Cypress console.
What I would like to have, is to change my code in a way that the message would be understandable. In the idea, it would be something like that:
const myCustomArray = [{ name: 'Lisa' }, { name: 'Katie' }];
cy.wrap(myCustomArray.some((user) => {
if (user.name === 'Lisa') {
return 'user name is Lisa';
}
})).should('eq', 'user name is Lisa');
But this can't work as .some() can only return a boolean. I suppose there is an array function that could help me do that, but I can't find which one.
I am not sure whether:
There are Cypress commands I am unaware of that could solve this issue, eg. customizing the assertion message.
Or it can just be solved with using JavaScript
Both solutions would be fine for me.

How about using .find() instead of .some(), and deep-eq the result,
cy.wrap(myCustomArray.find(user => user.name === 'Lisa'))
.should('deep.eq', { name: 'Lisa' });
ASSERT expected { name: Lisa } to deeply equal { name: Lisa }
or if you have big objects and just want to see the name,
cy.wrap(myCustomArray.map(user => user.name).find(name => name === 'Lisa'))
.should('eq', 'Lisa');
ASSERT expected Lisa to equal Lisa

Related

How can i use .map() only when there is an array?

I am new to react and I am passing an item prop. Some of the items has an empty array in items.modifiers. When I use an if an if condition i still get an error that "Cannot read property 'map' of undefined " Below is my code. Any help would be really appreciated.
const NewModal = ({ item }) => {
if (item.modifiers !== "") {
item.modifiers.map((modifier) => console.log(modifier.cat_name));
}
};
return [
{
items: [
{
item_id: 1,
item_name: "Philadelphia Steak Sandwich",
modifiers: {
cat_name: " Choose a side",
mod_items: [
{ mod_item_name: "French Fries", price: 1 },
{ mod_item_name: "Cole Slaw", price: 2 },
],
},
},
{
item_id: 2,
item_name: "Philadelphia Steak Sandwich Deluxe",
modifiers: "",
},
],
},
];
Use Array.isArray() first to check whether the item you're trying to map is of type Array.
const NewModal = ({item}) => {
if(Array.isArray(item.modifiers) {
item.modifiers.map((modifier)=> console.log(modifier.cat_name));
}
}
You need to ensure your data is an array in the first place. Then you can check whether the array is ready or not.
You should ensure data type is consistent, this is a given.
While waiting for the data to populate (probably from an API call), instead of if-else you can use null propagation operators / optional chaining
Then you can call something like this with a single question mark before the dot.
const newArray = item?.modifier?.map(item => do something)
This way it will suppress the undefined error.
Another step you need to take is conditional rendering.
For example
if (!data?.length) return null
//OR
if (!data?.length) return <div>data is loading</div>
You can simply do that by checking if every required condition is met and then by looping the modifiers array,
item && item.modifiers && item.modifiers.length && item.modifiers.map((modifier)=> console.log(modifier.cat_name))

Is there a better/shorter way to check a value of an object within an array which is part of an object in Javascript?

I'm sure this question must have been answered before, so a link (or what specifically to ask Google) would suffice, but what is the best way to do a check in a scenario like this (and does the new ? operator help in scenarios like this):
/**
* An API returns a job object like:
* { id: 123, name: 'The Job', details: [ { detail_name: "Foo", some: "thing" }, { detail_name: "Bar", some: "thing else" } ] }
*/
const fooDetail = job.details.find(attr => {
return attr.detail_name === 'Foo' });
if (fooDetail && fooDetail.detail_name === "Foo") {
// todo process `some: "thing"`
}
It seems long winded to have to find an object in an array based on a property, but then having to check again that the object exists before checking the property (or get a can't get detail_name of undefined error). Is there a better/shorter way?
You can use Array.some
This will return true if one is found..
The same callback as find can be used
const hasDetail = job.details.some(attr => {
return attr.detail_name === 'Foo' });
if (hasDetail) {
// todo process `some: "thing"`
}
If you're simply want to get the value and then later on check if the property exists, you can use find and ?. operator
const addDetail = job.details.find(attr => {
return attr.detail_name === 'Foo' });
if (addDetail?.detail_name === 'Foo') {
// todo process `some: "thing"`
}
You can do also
job.details.forEach(x=>{
if(x.detail_name == "Foo") {
console.log(x.some);
}
});

Filtering a JSON with Javascript

I am trying to filter a JSON using filter and I'm not getting the clubProducts to return as I hoped (allProducts works fine). Any help is appreciated. Thank you
const state = {
added: [],
all: [
{
id: 'bcd755a6-9a19-94e1-0a5d-426c0303454f',
name: 'Iced Coffee',
description: 'Coffee, now featuring ice.',
image: 'https://images.com',
price: 899,
fxCategory: 'Coffee'
},
{
id: 'cc919e21-9a19-94e1-ace9-426c0303454f',
name: 'The 2ndItem',
description: 'Wouldn't you like to know.',
image: 'https://images.com',
price: 499,
fxCategory: 'Club'
}
]
}
const getters = {
allProducts: state => state.all,
clubProducts: state => function () {
return state.all.filter(item => item.fxCategory == 'Club')
}
}
EDIT: Updated with latest attempt as per suggestions
You made two mistakes: you can use filter() only on an array (ie state.all in your case), and in your comparison you didn't quote the string 'Club'.
Also, your filter() can be written in a shorter way, as such:
clubProducts: state.all.filter(item => item.fxCategory == 'Club')
See documentation for more.
As mentioned by #Reyedy you can call filter function directly in 'clubProducts' field and this example is works.
But you may get an error because you use single quotes in 'description' field.
Try in state.all
description: "Wouldn't you like to know.",
instead of
description: 'Wouldn't you like to know.',
This is the only place I got an error trying to repeat your example.

mach beginning javascript with Array

Good afternoon, I am trying to make a method that tells me the number of elements an Array has that starts with "RT:"
For this I have developed the following code:
public getRowsRTs(idProyecto){
this.twitterService.getTargets().subscribe((data) => {
this.Twitter = data;
});
let countRT = this.Twitter.filter( tweet => tweet.message.startsWith("RT:")).length;
return countRT;
}
}
Here, data returns all the Documents that Mongo extracts, and puts them in the Twitter Array that I have defined at the beginning of the component. Within this Array each element has different attributes, such as _id, message, date ... I want you to tell me how many of those documents, the message value, begins with RT: and to return it to me, this code , it does not give me any problem, but it does not give me absolutely nothing, I do not know if someone could help me.
If the array is filled with strings, this should work:
let regixForRTStart = /^RT:/;
startArray = ['RT:1', 'RT:2', 'Other', 'Stuff'],
count = startArray.filter(item => regixForRTStart.test(item))
// length
console.log(count.length);
use filter and startsWith methods.
const arr = [
{ message: "abc" },
{ message: "RT:" },
{ message: "RT: 2" },
{ message: "test" }
];
const count = arr.filter(({ message }) => message.startsWith("RT:")).length;
console.log(count);

How to do a recursive prompt in Yeoman with promises?

I'm trying to figure out how to do a recursive prompt with a yeoman generator using promises. I am trying to produce a form generator that will first ask for a name for the form component and then ask for a name (that will be used as an id) for each input (ie: firstName, lastName, username, etc.). I've found answers for this question using callbacks but I would like to stick with promises. Below is the code I have so far and what I am attempting to do for the recursion but is not working. Any help and advice is appreciated thank you in advance!
const Generator = require('yeoman-generator')
const questions = [
{ type: 'input',
name: 'name',
message: 'What is the name of this form?',
default: 'someForm'
},
{
type: 'input',
name: 'input',
message: 'What is the name of the input?'
},
{
type: 'confirm',
name: 'askAgain',
message: 'Is there another input to add?'
}
]
module.exports = class extends Generator {
prompting() {
return this.prompt(questions).then((answers) => {
if (answers.askAgain) {
this.prompting()
}
this.userOptions = answers
this.log(this.userOptions)
})
}
}
For anyone that stumbles across this post looking for an answer this is what I ended up doing to make it work. As you can see in my Form class that extends Generator I have a method called prompting() in there. This is a method recognized by Yeoman's loop as a priority and it will not leave this method until something is returned. Since I'm returning a promise it will wait until my promise is finished before moving on. For my first prompt that's exactly what I need but for the second one to happen in prompting2 you can add
const done = this.async()
at the start of your method. This tells yeoman that you are going to have some asynchronous code happen and not to move past the method containing this until done is executed. If you do not use this and have another priority method in your class after it, such as writing() for when you are ready to produce your generated code, then yeoman will move past your method without waiting for your asynchronous code to finish. And you can see in my method prompting2() that I recursively call it whenever the user states that there is another input to name and it will continue doing so until they say there is not another input to name. I'm sure there is a better way to do this but it is working great for me this way. I hope this helps anyone that is looking for a way to do this!
const Generator = require('yeoman-generator')
const questions = [
{ type: 'input',
name: 'name',
message: 'What is the name of this form?',
default: 'someForm'
}
]
const names = [
{
type: 'input',
name: 'inputs',
message: 'What is the name of the input?',
default: '.input'
},
{
type: 'confirm',
name: 'another',
message: "Is there another input?",
default: true
}
]
const inputs = []
class Form extends Generator {
prompting() {
return this.prompt(questions).then((answers) => {
this.formName = answers.name
this.log(this.formName)
})
}
prompting2 () {
const done = this.async()
return this.prompt(names).then((answers) => {
inputs.push(answers.inputs)
if (answers.another) this.prompting2()
else {
this.inputs = inputs
this.log(this.inputs)
done()
}
})
}
}
module.exports = Form

Categories

Resources