How to validate multiple texts on a page in Cypress - cypress

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`)
})
})
})
})

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 correctly handle errors in this effect?

I've written the Effect below to make requests for each item in the array, and when one fails, the error isn't handled and the observable stream completes resulting actions not triggering effects any further.
Effect:
#Effect() myEffect$ = this.actions$.pipe(
ofType<myAction>(myActionTypes.myAction),
mergeMapTo(this.store.select(getAnArray)),
exhaustMap((request: any[]) => {
return zip(...request.map(item => {
return this.myService.myFunction(item).pipe(
map(response => {
return this.store.dispatch(new MyActionSuccess(response))
}),
catchError(error => {
return Observable.of(new MyActionFailure(error));
})
)
}))
})
How do I handle the error in this case?

finally() is not called when returning empty() in catch()

I wrote a generic request method that is supposed to show a loading indicator while the request is running. If an error occurs (like 404), I display the message in .catch and then return Observable.empty(), so that the following code doesn't crash (since no data is returned).
The big problem is that then .finally won't be called either. Is that a bug? Is there a workaround? Here's my code:
res = Observable
.of(true)
.do(() => this.store.dispatch(new ShowLoadingIndicatorAction()))
.switchMap(() => this.http.get(url, { headers: this.headers }))
.publishReplay(1)
.refCount()
.catch(error => {
this.messages.handleRequestError(error);
return Observable.empty();
})
.finally(() => this.store.dispatch(new HideLoadingIndicatorAction()));
// then later:
res.subscribe(doStuffWithData);
What RxJS-version are you using? It does works fine in this example:
const res$ = Rx.Observable
.of(true)
.switchMap(() => Rx.Observable.throw("Rest Error"))
.publishReplay(1)
.refCount()
.catch(error => Rx.Observable.empty())
.finally(() => console.log("Calling Finally!"));
res$.subscribe(console.info);
<script src="https://unpkg.com/rxjs/bundles/Rx.min.js"></script>

Resources