Cypress resets to cy.visit() after running each describe section - cypress

I have a spec file in Cypress below and every time it runs the spec, the "Analyze Section" succeeds but the "Other Section" fails due to it returning to the login page even though the before() hook at the root should just run once based on the level of nesting. I'm trying to make it so the login happens one time whenever any tests in this suite are run. Likewise, when any test in the "Analyze Section" are run we click the #HyperLinkAnalyze link one time to ensure we are on the proper page for any test. I was trying to make them cascade down but the beforeEach() call in each section ends up popping the page back out to the login page that happened in before().
context('Admin - Analyze Tab', { tags: ['#admin'] }, () => {
let user;
before(() => {
cy.visit(Cypress.env('admin_url'));
user = Cypress.env('admin_user');
cy.login(user.email, user.password);
});
describe('Analyze Section', ()=>{
beforeEach(() => {
cy.get('#HyperLinkAnalyze').click();
cy.get('#HyperLinkCampaignStats').click();
});
it('TEST 1', {}, () => {
cy.contains('#analytics-row1', 'Response Rate').should('be.visible');
});
it('TEST 2', {}, () => {
cy.contains('#analytics-row1', 'Response Rate').should('be.visible');
});
});
describe('Other Section', ()=>{
beforeEach(() => {
cy.get('#HyperLinkAnalyze').click();
cy.get('#HyperLinkXSellStats').click();
});
it('TEST 1', {}, () => {
cy.contains('#analytics-row1', 'Response Rate').should('be.visible');
});
it('TEST 2', {}, () => {
cy.contains('#analytics-row1', 'Response Rate').should('be.visible');
});
});
});
```js

You can try Cypress.session .
The new cy.session() command solves problem by caching and
restoring cookies, localStorage and sessionStorage after a successful
login. The steps that your login code takes to create the session will
only be performed once when it's called the first time in any given
spec file. Subsequent calls will restore the session from cache.
Set experimentalSessionSupport flag to true in the Cypress config or by using Cypress.config() at the top of a spec file.
Check below example -
const loginWithSession = () => {
cy.session(() => {
cy.visit(Cypress.env('admin_url'));
let user = Cypress.env('admin_user');
cy.login(user.email, user.password);// Can be parameterize
});
cy.visit(Cypress.env('admin_url'));//Or home page url
})
}
context('Admin - Analyze Tab', { tags: ['#admin'] }, () => {
before(() => {
loginWithSession();
});
describe('Analyze Section', ()=>{
beforeEach(() => {
loginWithSession();
cy.get('#HyperLinkAnalyze').click();
cy.get('#HyperLinkCampaignStats').click();
});
it('TEST 1', {}, () => {
cy.contains('#analytics-row1', 'Response Rate').should('be.visible');
});
it('TEST 2', {}, () => {
cy.contains('#analytics-row1', 'Response Rate').should('be.visible');
});
});
describe('Other Section', ()=>{
beforeEach(() => {
loginWithSession();
cy.get('#HyperLinkAnalyze').click();
cy.get('#HyperLinkXSellStats').click();
});
it('TEST 1', {}, () => {
cy.contains('#analytics-row1', 'Response Rate').should('be.visible');
});
it('TEST 2', {}, () => {
cy.contains('#analytics-row1', 'Response Rate').should('be.visible');
});
});
});
In older version of Cypress you can use https://docs.cypress.io/api/cypress-api/cookies#Preserve-Once to preserve the cookies and Cypress will not clear it .
beforeEach(() => {
Cypress.Cookies.preserveOnce('session_id', 'remember_token')
})

Related

How to preserve login in cypress e2e tests?

I have implemented some cypress e2e tests.
But, before each, it() block it performs login step.
I want to make login once for every test suit.(for decreasing tests run time)
my tests structure is as below :
describe('Main Suit', () => {
before(() => {
cy.visit('/register')
// Steps to register
})
beforeEach(() => {
cy.visit('/login')
cy.get('#email').type('test12#gmail.com');
cy.get('#password').type('password')
cy.get('.p-button').click()
cy.wait(2000)
})
describe('test suit - 1', () => {
it('test - 1', () => {
cy.visit('/somePath')
cy.get('table').contains('td', 'No data found.');
cy.wait(2000)
})
it('test - 2', () => {
cy.visit('/somePath')
cy.get('table').contains('td', 'No data found.');
cy.wait(2000)
})
it('test - 3', () => {
cy.visit('/somePath')
cy.get('table').contains('td', 'No data found.');
cy.wait(2000)
})
});
describe('test suit - 2', () => {
it('test - 1', () => {
cy.visit('/somePath')
cy.get('table').contains('td', 'No data found.');
cy.wait(2000)
})
it('test - 2', () => {
cy.visit('/somePath')
cy.get('table').contains('td', 'No data found.');
cy.wait(2000)
})
it('test - 3', () => {
cy.visit('/somePath')
cy.get('table').contains('td', 'No data found.');
cy.wait(2000)
})
});
});
I have tried cy.session().
But, It did not worked.
my cypress version is ^10.4.0
Thanks in advance.
The cy.session() command is designed to do exactly what you want.
If it did not work, perhaps you are using it incorrectly?
A common mistake is to call it just once. Instead it must be in beforeEach(), but it does not perform the login for each test - the first test it will run setup(), for each other test it will just restore the login credentials (either cookies, localstorage, or session storage).
beforeEach(() => {
const setup = () => {
cy.visit('/login')
cy.get('#email').type('test12#gmail.com');
cy.get('#password').type('password')
cy.get('.p-button').click()
})
cy.session('login', setup())
})
Thanks all for help.
After digging deep.
I have found the solution.
I have added below statement under cypress.config.js file under e2e block :
experimentalSessionAndOrigin: true,
And added below block of code before all test suits :
beforeEach(() => {
cy.session('user', () => {
cy.visit('/login');
cy.get('#email').type('test12#gmail.com');
cy.get('#password').type('password');
cy.get('.p-button').click();
cy.wait(2000);
});
});

use beforeEach on each test

I need to call a function cy.restoreLocalStorage(); on each describe in Cypress to restore my local storage for the test:
describe('Create a business rule', () => {
beforeEach(() => {
cy.restoreLocalStorage();
});
it('Navigate to business rules', () => {
cy.el('btnCompanySettings').click({ force: true });
cy.url().should('include', 'dm/settings/general');
cy.el('selectBreadcrumbs').click().find('.scrollable-content').children().contains('Bedrijfsregels').click();
});
});
describe('Create a business rule', () => {
beforeEach(() => {
cy.restoreLocalStorage();
});
it('Navigate to groups', () => {
cy.el('selectBreadcrumbs').click().find('.scrollable-content').children().contains('Groepen').click();
});
});
I don't want to define this for each describe though, is it possible to define this somewhere else so it runs implicitly?
I've tried before:spec in my plugin file:
on('before:spec', () => {
console.log('############ BEFORE SPEC ###############');
});
But that only runs before the spec, not before each test.
You can add the beforeEach() under the cypress/support/e2e.js This will run the same beforeEach before all your tests.

Import method in beforeEach

To run a set of tests I have to create an account, for that I have to use beforeEach(), but if I have a lot of test specs how to organize my test structure to avoid duplicates:
describe('my form', () => {
beforeEach(() => {
cy.visit('/users/new')
cy.get('#first').type('Johnny')
cy.get('#last').type('Appleseed')
cy.get('button').click()
})
Is it possible to do something like this and just import createAccount() method everywhere:
describe('my form', () => {
beforeEach(() => {
createAccount()
})
You can use cypress custom commands and achieve this. Go to cypress/support/commands.js and write:
Cypress.Commands.add('createAccount', (firstName, lastName) => {
cy.get('#first').type(firstName)
cy.get('#last').type(lastName)
cy.get('button').click()
})
Now in your tests, you can use it like:
describe('my form', () => {
beforeEach(() => {
cy.visit('/users/new')
cy.createAccount('Johnny','Appleseed')
})
})

How to search through JSON response with Cypress assertions

Considering the below API response I would like to assert the exact location of a certain value in a JSON structure. In my case the name of pikachu within forms:
"abilities": [
{
"ability": {
"name": "lightning-rod",
"url": "https://pokeapi.co/api/v2/ability/31/"
},
"is_hidden": true,
"slot": 3
},
{
"ability": {
"name": "static",
"url": "https://pokeapi.co/api/v2/ability/9/"
},
"is_hidden": false,
"slot": 1
}
],
"base_experience": 112,
"forms": [
{
"name": "pikachu",
"url": "https://pokeapi.co/api/v2/pokemon-form/25/"
}]
I would like to extend below code snippet to not scan the entire body as a whole as there are a lot of name's in the response, but rather go via forms to exactly pinpoint it:
describe('API Testing with Cypress', () => {
var baseURL = "https://pokeapi.co/api/v2/pokemon"
beforeEach(() => {
cy.request(baseURL+"/25").as('pikachu');
});
it('Validate the pokemon\'s name', () => {
cy.get('#pikachu')
.its('body')
.should('include', { name: 'pikachu' })
.should('not.include', { name: 'johndoe' });
});
Many thanks in advance!
Getting to 'forms' is just a matter of chaining another its(), but the 'include' selector seems to require an exact match on the object in the array.
So this works
it("Validate the pokemon's name", () => {
cy.get("#pikachu")
.its("body")
.its('forms')
.should('include', {
name: 'pikachu',
url: 'https://pokeapi.co/api/v2/pokemon-form/25/'
})
})
or if you just have the name,
it("Validate the pokemon's name", () => {
cy.get("#pikachu")
.its("body")
.its('forms')
.should(items => {
expect(items.map(i => i.name)).to.include('pikachu')
})
})
and you can assert the negative,
.should(items => {
expect(items.map(i => i.name)).to.not.include('johndoe')
})
Can you try the below code and see if it helps with your expectation. From the response you could get the name as below;
describe('API Testing with Cypress', () => {
var baseURL = "https://pokeapi.co/api/v2/pokemon"
beforeEach(() => {
cy.request(baseURL+"/25").as('pikachu');
});
it('Validate the pokemon\'s name', () => {
cy.get('#pikachu').then((response)=>{
const ability_name = response.body.name;
expect(ability_name).to.eq("pikachu");
})
});
})

How set an authenticated user in Parse tests?

I've some tests configured for Parse, but I don't know how to set the authenticated user in Parse.Cloud.run:
it('set user authenticated', function(done) {
var user = new Parse.User({username: 'example2', password: 'example'});
user.save();
Promise.resolve()
.then(function() {
return Parse.Cloud.run('thread', { user: { id: user.id } });
})
});
So in the cloud function I've request.user.id and it throws Undefined request.user
More info: https://github.com/ParsePlatform/parse-server/issues/2488
There's an error in how you are using promises in your code above:
user.save();
Promise.resolve()
.then(function() {
return Parse.Cloud.run('thread', { user: { id: user.id } });
})
You don't want to use Promise.resolve().then(). You would want user.save().then()
Below are two sets of working unit test that I think exercises what you want.
This first one passes the user id as a parameter:
describe('pass parameters to cloud code', () => {
// set up the cloud code function
beforeEach(() => {
Parse.Cloud.define('thread', (request, response) => {
// just return the user id that passed in so we can test it.
response.success(request.params.user.id);
});
});
it('should pass parameters', (done) => {
const user = new Parse.User({
username: 'example2',
password: 'example'
});
user.save()
.then(() => Parse.Cloud.run('thread', { user: { id: user.id } }))
.then((result) => expect(result).toBe(user.id))
.then(done)
.catch(done.fail);
});
});
And this second set of tests shows how an authenticated user is passed to cloud code.
describe('user in cloud code unit test', () => {
beforeEach(() => {
Parse.Cloud.define('thread', (request, response) => {
const result = request.user
? request.user
: 'no user set';
response.success(result);
});
});
it('should not have a user', (done) => {
Parse.Cloud.run('thread')
.then(result => expect(result).toBe('no user set'))
.then(done)
.catch(done.fail);
});
it('should get the logged in user', (done) => {
Parse.User.signUp('Alice', 'Password', { name: 'value' })
// note that we don't have to pass the logged in user
// if there is a logged in user, it'll be in request.user
.then(() => Parse.Cloud.run('thread'))
.then(userAsResult => expect(userAsResult.get('username', 'Alice')))
.then(done)
.catch(done.fail);
});
});

Resources