Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent full page reload when testing Angular with Cypress?

When I write my Cypress e2e tests for my Angular application, I often use the visit() command like this:

.visit('/foo/bar')

This get's the job done, i.e. Cypress navigates to /foo/bar, but the entire application reloads. This is very slow, and does not mimic actual user behaviour.

Is it possible to navigate/visit the Angular application, without full page reloads?

I did try:

cy.window().then((win) => {
   win.history.pushState({}, '', '/foo/bar')
})

But angular does not react to this.

like image 984
DauleDK Avatar asked Mar 21 '19 15:03

DauleDK


People also ask

Can we test angular application with Cypress?

Install Cypress in Angular Project The above command will help open a chrome browser to check the cypress test cases like the below screen. Click on the spec. ts file, and it will run the cypress test cases for that file. It will run in a terminal and share the spec success and failure report.

What does Cy reload do?

cy. reload() can time out waiting for the page to fire its load event. cy. reload() can time out waiting for assertions you've added to pass.


2 Answers

I solved this by adding a custom cypress command that calls a method on the Angular applications app.component.ts. The solution look like this Updated to Ivy:

app.component.ts

export class AppComponent {
    constructor(
        private router: Router,
        private ngZone: NgZone,
    ) {}

    // Method Cypress will call
    public navigateByUrl(url: string) {
        this.ngZone.run(() => {
            this.router.navigateByUrl(url);
        });
    }
}

cypress/support/commands.ts

// add new command to the existing Cypress interface
declare global {
    namespace Cypress {
        interface Chainable {
            visitAngular: (url: string) => Chainable<Window>;
        }
    }
}

// Custom function
export function visitAngular(url: string) {
    cy.get('body').then($body => {
        try {
            const el = $body.find('app-root')[0];
            const win = el.ownerDocument.defaultView;
            const componentInstance = win.ng.getComponent(el);
            cy.log(`Angular nav to '${url}' `);
            componentInstance.navigateByUrl(url);
            cy.url().should('contain', url);
        } catch (error) {
            cy.log(`Cypress nav to '${url}' `);
            cy.visit(url);
        }
    });
}

Cypress.Commands.add('visitAngular', visitAngular);

cypress/support/index.d.ts

interface Window {
    ng: {
        getComponent: (element: any) => any;
    };
}

We have used this for 2 months now, and it works great in local development, speeding up test executions with x3. But in CI it's another story.

like image 100
DauleDK Avatar answered Nov 10 '22 14:11

DauleDK


You can make it work in a CI enviroment registering a global function an call that instead of the angular component:

app.component.ts

export class AppComponent {
    constructor(
        private router: Router,
        private ngZone: NgZone,
    ) {
        // Method Cypress will call
        if ((window as any).Cypress) {
            (window as any).cypressNavigateByUrl = (url: string) => this.cypressNavigateByUrl(url);
        }
    }

    public cypressNavigateByUrl(url: string) {
        this.ngZone.run(() => {
            this.router.navigateByUrl(url);
        });
    }
}

cypress/support/commands.ts

Cypress.Commands.add('visitAngular', (url) => {
    cy.window().then((win) => {
        win.cypressNavigateByUrl(url);
    });
});
like image 24
Diego Busacca Avatar answered Nov 10 '22 14:11

Diego Busacca