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);
});
});
Related
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.
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')
})
I am trying to validate multiple texts on a web page. When I use the invoke method it is giving me 'Maximum call stack size exceeded error' because of multiple function calls.
How can I limit the use of .then() calls?
Below is my cypress code and I have more than 25 text validations to do
Cypress._.times(5, () => {
Elements.flow.gettablerow().eq(Elements.getrandomnumber()).within($row => {
Elements.flow.getcheckbox().check({ force: true })
Elements.flow.getbutton().click().then($el => {
Elements.flow.getheader().invoke('text').then($text => {
expect($text).to.equals('header text')
})
Elements.flow.getwarningtext().invoke('text').then(text => {
expect(text).to.equals('warning text')
})
Elements.flow.getvelocitytext().invoke('text').then($text => {
expect($text).to.equals(`velocit text`)
})
})
})
})
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')
})
})
i have a basic custom element.
//my-component.html
<template>
<input type="text" id="my-input" value.bind="searchText">
</template>
//my-component.js
export class MyComponent {
searchText = 'initial text';
}
what i am trying to assert in my unit test is that when i update the value of my input field (id=my-input) in the dom the correct bound value (searchText) is also updated.
heres my test (jasmine/karma)
import {StageComponent} from 'aurelia-testing';
import {bootstrap} from 'aurelia-bootstrapper';
describe('some desc', () => {
let component;
beforeEach(() => {
component = StageComponent
.withResources('src/my-component')
.inView('<div><my-component></my-component></div>');
});
it('some it', done => {
component.create(bootstrap).then(() => {
const inpuElement = document.querySelector('#my-input');
inpuElement.value = 'new text';
//component.viewModel.searchText = 'new text';
expect(component.viewModel.searchText).toBe('new text');
done();
}).catch(e => { console.log(e.toString()) });
});
afterEach(() => {
component.dispose();
});
});
when i run this the test fails with
Expected 'initial text' to be 'new text'
to get it passing i need to replace
component.viewModel.searchText = 'new text';
with
inpuElement.value = 'new text';
i really dont want to do this as i am trying to mimic the user inputing text directly into the input field.
can this be done and if so what am i missing?
I guess you are looking for something like this: https://github.com/aurelia/testing/issues/70
Aurelia is observing the DOM events to update the VM. As you do not trigger any event, the VM is not "notifying" the change. Do something like this (assuming you use aurelia-pal):
import {StageComponent} from 'aurelia-testing';
import {bootstrap} from 'aurelia-bootstrapper';
import {DOM} from 'aurelia-pal';
describe('some desc', () => {
let component;
beforeEach(() => {
component = StageComponent
.withResources('src/my-component')
.inView('<div><my-component></my-component></div>');
});
it('some it', done => {
component.create(bootstrap).then(() => {
const inputElement = DOM.getElementById('my-input');
inputElement.value = 'new text';
inputElement.dispatchEvent(DOM.createCustomEvent('change'));
setTimeout(() => {
expect(component.viewModel.searchText).toBe('new text');
done();
}, 50);
}).catch(e => { console.log(e.toString()) });
});
afterEach(() => {
component.dispose();
});
});