Cypress with SystemJS
Asked Answered
E

2

5

I am attempting to create some basic tests to try out the new Cypress library. In my test I have cy.visit('http://mywebsite.com'); which is loading an AngularJS app that uses SystemJS.

If I understand Cypress correctly, I shouldn't have to do anything else and it will make sure the page is loaded before running anything else. However this doesn't seem to be working because the page is loaded, but SystemJS is still loading the modules.

How can I get Cypress to wait for all the SystemJS modules to load before running any more tests without using cy.wait(5000)?

EDIT

Thanks to Dwelle this is the solution that works for me. I wrap the initial System.import in a promise that gets resolved once the AngularJS app has been bootstrapped.

window.APP_READY = new Promise(function(resolve, reject) {
    System.import('app').then(function(app) {
        angular.element(document).ready(function() {
            angular.bootstrap(document, ['app']);
            resolve();
        });
    });
});

And then in the test

cy.visit('http://mywebsite.com').its('APP_READY');
Enamel answered 15/11, 2017 at 5:24 Comment(0)
P
4

Not familiar with SystemJS or your app, but if we assume you're doing some asynchronous work on load, you can set up some global property which indicates whether the app is ready or not.

// your main.js
let _appReadyResolver;
window.APP_READY = new Promise( resolve => _appReadyResolver = resolve );

// do some async setup
setTimeout(() => {

    _appReadyResolver();
});

Then, in your tests:

cy.visit("/")
    // by default will wait 4sec for APP_READY prop to exist on
    //  window object (unfortunately I don't know how to increase timeouts
    //  of `cy.its` command)
    // After that, it will wait indefinitely for your promise to resolve
    .its("APP_READY")

That being said --- if you're not doing any async setup in your app, but the main.js is simply being loaded asynchronously and it can take longer than 4sec, then I'd do this:

// index.js
<script>
  SystemJS.import('/js/main.js');
  window.APP_READY = new Promise( resolve => {
    let interval = setInterval(() => {
        if ( window.MAIN_READY ) {
            resolve();
            clearTimeout(interval);
        };
  }, 100 );
</script>

// main.js
window.MAIN_READY = true;

You'll want to strip the APP_READY logic from production build.

Piero answered 15/11, 2017 at 9:51 Comment(1)
If its's 4 sec is not enough, you can render a hidden element to the <body> with the status and check that with .get() where you can increase the timeout with an option object as the 2nd argument: cy.get('#main-status', { timeout: 8000 }).should('have.attr', 'data-cy-status', 'ready')Kavita
S
0

In theory, you can also solve this in a pure Cypress way.

If your page makes any request which indicates that your app is ready, you can make Cypress wait for it to complete before it proceeds. You might find this cleaner than the APP_READY work-around. Its just an opinion though, as this approach will fail if there are not requests you can trust to signal that your app is ready.

cy.server();
cy.route('**/api/getData').as('getData');
cy.visit('/home');
cy.wait('@getData');

Cypress best practices: Unnecessary-Waiting.

Cypress docs on wait Alias.

Sisely answered 8/10, 2018 at 12:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.