When using cypress.io to test an angular web page, whats the best / most reliable way to detect when the page is fully loaded and idle. Not just the onload event. Needs to include all XHR requests, angular digest cycles complete and all rendering complete including all animations complete.
The reason is that at this point I want to test that the page does NOT contain an element and cant test that until all the above is fully complete.
Wait for API response wait() command. Test will only continue once that command is finished. Finding the right request to intercept is a great way to make sure that Cypress will wait until page loads with all the right data loaded.
You should just do a cy. get() and . should() to make Cypress wait for something to appear on the page.
If you are waiting for some resources to be loaded in your app, you can intercept a request and then create an alias for it. That alias will then be used with . wait() command. Test will only continue once that command is finished.
wait() goes through two separate "waiting" periods. The first period waits for a matching request to leave the browser. This duration is configured by the requestTimeout option - which has a default of 5000 ms.
For the record what we did for this (for AngularJS) was to add a new waitforpageidle
command that can be used like this:
cy.waitforpageidle();
Typescript is as follows:
Cypress.Commands.add(
"waitforpageidle",
() => {
console.warn("Waiting for page idle state");
const pageIdleDetector = new PageIdleDetector();
pageIdleDetector.WaitForPageToBeIdle();
}
);
Where the PageIdleDetector is as follows:
export class PageIdleDetector
{
defaultOptions: Object = { timeout: 60000 };
public WaitForPageToBeIdle(): void
{
this.WaitForPageToLoad();
this.WaitForAngularRequestsToComplete();
this.WaitForAngularDigestCycleToComplete();
this.WaitForAnimationsToStop();
}
public WaitForPageToLoad(options: Object = this.defaultOptions): void
{
cy.document(options).should((myDocument: any) =>
{
expect(myDocument.readyState, "WaitForPageToLoad").to.be.oneOf(["interactive", "complete"]);
});
}
public WaitForAngularRequestsToComplete(options: Object = this.defaultOptions): void
{
cy.window(options).should((myWindow: any) =>
{
if (!!myWindow.angular)
{
expect(this.NumberOfPendingAngularRequests(myWindow), "WaitForAngularRequestsToComplete").to.have.length(0);
}
});
}
public WaitForAngularDigestCycleToComplete(options: Object = this.defaultOptions): void
{
cy.window(options).should((myWindow: any) =>
{
if (!!myWindow.angular)
{
expect(this.AngularRootScopePhase(myWindow), "WaitForAngularDigestCycleToComplete").to.be.null;
}
});
}
public WaitForAnimationsToStop(options: Object = this.defaultOptions): void
{
cy.get(":animated", options).should("not.exist");
}
private getInjector(myWindow: any)
{
return myWindow.angular.element(myWindow.document.body).injector();
}
private NumberOfPendingAngularRequests(myWindow: any)
{
return this.getInjector(myWindow).get('$http').pendingRequests;
}
private AngularRootScopePhase(myWindow: any)
{
return this.getInjector(myWindow).get("$rootScope").$$phase;
}
}
You can make Cypress wait for any request to complete before it proceeds. So if you want to wait for all XHR of a certain page, you can do the following for each of them. How long it waits is defined by the responseTimeout configuration.
cy.server();
cy.route('**/api/getData').as('getData');
cy.visit('/home');
cy.wait('@getData');
Or to wait for several routes:
cy.server();
cy.route('**/api/getData').as('getDataX');
cy.route('**/api/getData').as('getDataY');
cy.visit('/home');
cy.wait(['@getDataX', '@getDataY']);
Cypress best practices: Unnecessary-Waiting.
Cypress docs on wait Alias.
For Angular (2+)
The previous answer relates to AngularJS. For Angular 2+, you can use this other solution.
Disclaimer : In most cases Cypress waiting mechanism is sufficient. But if you really need the application to stabilise (ie: that all requests and Async Tasks like debounces have fulfilled) you can use this command.
/**
* Used by Cypress.waitForAngularIdle() (in commands.ts) to wait for all Pending request to finish
*/
function ngIsStable() {
const test = (window as any).getAngularTestability(window.document.querySelector('body div')) as Testability // you need to select an element managed by Angular. Usually any div in your html body
const stable = test.isStable()
//if (!stable) { // this console.log block is just for information, you can remove it
console.log('Angular is ' + (stable ? '' : 'NOT ') + 'stable:', {
pendingRequestCount: test.getPendingRequestCount(),
_pendingCount: (test as any)._pendingCount,
pendingTasks: (test as any).getPendingTasks(),
})
// }
return stable
}
window['ngIsStable'] = ngIsStable;
function waitForAngularIdle() {
cy.window().invoke('ngIsStable').should('be.true')
}
Cypress.Commands.add('waitForAngularIdle', waitForAngularIdle);
declare namespace Cypress {
interface Chainable<Subject = any> {
waitForAngularIdle()
}
}
cy.waitForAngularIdle()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With