How could I do not repeat the selection process in Cypress? - testing

How could I do not repeat the selection process in Cypress?
E.g. if I have:
cy
.get("...").find("...")...eq(0)...should("...")
.get("...").find("...")...eq(1)...should("...");
How could I avoid duplicating the .get("...").find("...")... part if at some point I need to pick either the eq(0) or the eq(1)?

You can use .as() to alias an element.
// Just aliasing the base
cy.get('foo').find('bar').as('something');
cy.get('#something').eq(0).should('exist');
cy.get('#something').eq(1).should('exist');
// aliasing the specific elements
cy.get('foo').find('bar').eq(0).as('firstEl');
cy.get('#firstEl').should('exist');
cy.get('foo').find('bar').eq(1).as('secondEl');
cy.get('#secondEl').should('exist');
You could also use a custom command.
// If the selectors in `get` and `find` are constant, you could do a custom command
Cypress.Commands.add('myGet', (index) => {
return cy.get('foo').find('bar').eq(index);
})
cy.myGet(0).should('exist');
// Or if you wanted to be able to customize the get and find
Cypress.Commands.add('myGet', (get, find, index) => {
return cy.get(get).find(find).eq(index);
})
cy.myGet('foo', 'bar', 0).should('exist');

You can create a custom command for this. Go to cypress/support/commands.js and write:
Cypress.Commands.add('selectElement', (index) => {
cy.get('selector').find('selector').eq(index).should('be.visible')
})
And then in your test just write:
cy.selectElement(1)
cy.selectElement(2)

Related

How to chain a custom function to express-validator?

I am using express-validator and I have chained some validations to link parameter like this:
route.post('/landing-pages/:link/blocks',
[
param('link').trim().escape().isString()
],
controller.addBlocks);
I need to add some chained functions like trim and escape to be able to modify the value.
I can use custom method like the following to add new validation:
route.post('/landing-pages/:link/blocks',
[
param('link').trim().escape().isString().custom((value, { req, location, path }) =>
{
//return true/false based on custom validation.
}
],
controller.addBlocks);
But instead of validating and returning true/false, I want to modify value and change it's original source exactly the way that trim or escape is doing it. for example, I want to replace some characters or I want to remove some words, etc.
Is there anyway to do it with express-validator?
you can chain customSanitizer function for that purpose
param('link').trim().escape().isString().customSanitizer(value => {
// imagine we have a sanitizer function
const sanitizedLink = linkSanitizer(value)
return sanitizedLink;
})

Understanding then() in Cypress

I am reading through the documentation in Cypress and I think I have an idea as to what then() does. It works like promises, where a promise returns another promise, but with then(), we are returning a new subject.
If we look at the code example below, we are using then() because we are returning a new variable, which in this case is called target.
Am I understanding this correctly? If not, can someone correct me?
it.only('Marks an incomplete item complete', () => {
//we'll need a route to stub the api call that updates our item
cy.fixture('todos')
.then(todos => {
//target is a single todo, taken from the head of the array. We can use this to define our route
const target = Cypress._.head(todos)
cy.route(
"PUT",
`api/todos/${target.id}`,
//Here we are mergin original item with an object literal
Cypress._.merge(target, {isComplete: true})
)
})
.then is used to receive the results from cy.fixture('todos'). The variable target is not significant in this code.
In your code sample, the variable that is returned from cy.fixture is named todos - the spacing of the code may be throwing you off here? The .then call is attached to the cy.fixture() call
// These 2 code blocks are the same - just different spacing
cy.fixture('todos')
.then(todos => {});
cy.fixture('todos').then(todos => {});
https://docs.cypress.io/api/commands/fixture.html#Usage
cy.fixture('logo.png').then((logo) => {
// load data from logo.png
})
Using .then() allows you to use the yielded subject in a callback function and should be used when you need to manipulate some values or do some actions.
To put it simply, it is used to play around with the yield of the previous command and work around with it in that case. THEN() command is handy and helpful in debugging the yield of the previous command.
const baseURL = "https://jsonplaceholder.typicode.com";
describe("Get Call-Expect+ normal req", () => {
it("GetPostById-Expect", () => {
cy.request(baseURL + "/posts/1").as("GetPostById");
cy.get("#GetPostById").then((response) => {
//response: status
expect(response.status).to.equal(200);
expect(response.status).to.eq(200);
});
});
Refer: https://docs.cypress.io/api/commands/then#Promises

Cypress Get Attribute value and store in Variable

I want to get the Attribute value and store in a variable how we can achieve this in cypress
In my case I want to get the complete class value and store it in variable.
This code just give me the attribute class value but how I can store the fetch value in variable
cy.get('div[class*="ui-growl-item-container ui-state-highlight ui-corner-all ui-shadow ui-growl-message"]').invoke('attr', 'class')
I was trying to compare the style of one element with another to make sure they were equal. Here's the code that seems to work for me.
cy.get('.searchable-group-selector-card-image')
.eq(4)
.invoke('attr', 'style')
.then(($style1) => {
const style1 = $style1
})
A good way to solve this kind of scenario is to use the alias mechanism. One could leverage this functionality to enqueue multiple elements and then check all of them together by chaining the results. I've recently come to a case in an SPA where the assertion had to happen between elements that were spread across different angular routes (call them different pages).
In your use case, this would like:
cy.get('.searchable-group-selector-card-image')
.eq(4)
.invoke('attr', 'style')
.as('style_1')
cy.get('.another-element')
.invoke('attr', 'style')
.as('style_2')
// later on for example you could do
cy.get('#style_1').then(style_1 => {
cy.get('#style_2').then(style_2 => {
// Both values are available and any kind of assertion can be performed
expect(style_1).to.include(style_2)
});
});
This is described in Variables and Aliases section of the Cypress Documentation.
Here is how I got the value of for attribute in a label tag which had text "Eat" inside.
cy.contains('Eat').then(($label) => {
const id = $label.attr('for');
}
Most important thing is to get the selector right, so it exactly finds the value you are looking for. In this case you already found it. By using then() gives you the ability to store it in a variable.
cy.get('div[class*="ui-growl-item-container ui-state-highlight ui-corner-all ui-shadow ui-growl-message"]').invoke('attr', 'class')
.then($growl-message => {
const message = $growl-message.text()
//do the checks with the variable message. For example:
cy.contains(message)
})
Note that the scope of the variable is within the curly brackets. Thus using the variable has to be within those curly brackets.

How can I check an element for multiple CSS classes in Cypress?

How to check if an element has multiple classes?
Couldn't find anything on the official docs, only:
cy.get('form').find('input').should('have.class', 'disabled')
or
expect($el).to.have.class('foo')
When inserting multiple class names, I get an error:
expect($el).to.have.class('foo bar baz')
Is there a solution?
One way is to chain multiple assertions together using cy.and():
cy.get('div')
.should('have.class', 'foo')
.and('have.class', 'bar')
.and('have.class', 'baz')
One can certainly use a little helper function BUT it only makes sense with longer class lists as it adds two more lines to your test code.
Helper function:
function shouldHaveClasses(el, classList = []) {
classList.forEach((c) => {
expect(el).to.have.class(c)
})
}
Your test:
describe('My Test', () => {
cy.get('div')
.then((el) => {
shouldHaveClasses(el, ['foo', 'bar', 'baz'])
})
})

Find matching elements in array and do something for each match

I'm a little overwhelmed by all the functions available to me in Lodash, so I hope someone can point me to the one I'm sure exists which will do the following for me. I want to be able to pass an array and a search condition, and have it loop through all the matched items, allowing me to run a function for each. What I have at the moment is something akin to this:
_.each(myArray, (item) => {
if (item.field=="whatever") {
console.log("Matched by "+item.name);
}
});
This works fine, of course. It's just that I'm sure Lodash has a way for me to move the item.field=="whatever" into the function arguments somehow, and I'd prefer to go with the more idiomatic Lodash way if I can.
It's just that I'm sure Lodash has a way for me to move the item.field == "whatever" into the function arguments somehow
If you want to find all the matching items in an array based on the arguments you pass in, then you could use the _.filter method, which can use the _.matches shorthand internally:
_.filter(myArray, { field: 'whatever' });
However, you would still need to loop over the items if you want to do something for each match:
_.each(_.filter(myArray, { field: 'whatever' }), item => {
console.log("Matched by " + item.name);
});
Alternatively, if you want a different way of writing this, you can wrap the filtered items with the lodash object wrapper, _(), which essentially enables chaining, thereby allowing you to chain the _.each() method:
_(_.filter(myArray, { field: 'whatever' })).each(item => {
console.log("Matched by " + item.name);
});
Or a more readable version:
var matchedItems = _.filter(myArray, { field: 'whatever' });
_(matchedItems).each(item => {
console.log("Matched by " + item.name);
});
Personally, I would probably just keep what you originally wrote since it's short, readable and easy to maintain.