Cypress Angular When Can Test Start?

Viewed 800

I'm using Cypress to do end-to-end testing on my Angular application. One source of flake that I have observed is that Cypress is fast to act and my application is (relatively) slow to become ready for interaction. Cypress has some oft-referenced blog posts that discuss this quandary.

The example in one of the blog posts suggests hijacking the addEventListener to handle a very specific example. But what I am looking for is the appropriate "sledgehammer" for Angular... anyone have any bright ideas / techniques that don't involve adding a 10 second wait to every page visit before engaging test activity (which by the way does work)? I have spent considerable time Googling to no avail. Thanks in advance for your ideas!

Aside: I've also found this open issue that, were it to be resolved fruitfully might be part of the answer.

2 Answers

I've encountered this issue myself and we've tried many different things at work on our app. Even though it's something which needs to be adapted, we're quite happy with our current solution.

But before I get to the solution, let me explain why this is a hard problem to solve without simply having a long wait before we start testing.

First of all, we need to wait for our page to be ready. Luckily for us, Cypress does that for us.

Then, we could wait for Angular to be bootstraped. One way of being aware of that is by checking if getAllAngularRootElements is available on the window object. And yes this function is apparently there even in prod mode!

  cy.window()
    .its('getAllAngularRootElements' as any)
    .should('exist');

But this isn't a perfect solution especially if... You use lazy loading.

Just because Angular bootstraped your app doesn't mean all your lazy modules are loaded! And this is where it gets really unlikely to find an automatic way of waiting for exactly what you want to be ready.

So how do we start testing as soon as we can and in a safe way? In my opinion the best option to do so is to have a check after every cy.visit call and wait for one element around what you're about to test, to exist. Example:

cy.visit(`some-page`);

cy.get(
  '.some-class-around-what-I-want-to-test',
  { timeout: 15000 }
).should('exist');

// ... rest of the test goes here

The code above will load the page, wait for the element you're targetting to exist for a maximum of 15 seconds but the important thing to note is that as soon as this element is available, it'll consider that check successful and move on. So if your app takes 2 seconds to display that element, your tests will starts straight after!

If you want to make it less repetitive for all your tests, you can create a function to open a given page and wait for its content to be there before doing something else. Example:

// at the top of the test file
const openProfilePage = () => {
  cy.visit('profile');
  cy.get(
    '.profile',
    { timeout: 15000 }
  ).should('exist');
}

// in every tests after or in a before each
openProfilePage();

// test something else

Using cy.visit has always worked for me, just make sure to setup app routes, authentication cookies and mocked responses beforehand as mentioned in the docs.

Probably a solution wokring for you will be found in passing the correct options to visit(see Arguments)

Helpful wrapper around visiting your sites routes

waitForAppToStart: () => {
  cy.window().then(
      { timeout: 120000 }, // should definitely be enough
      win => new Cypress.Promise(
          (resolve, reject) => win.requestIdleCallback(resolve)));
},

visitSite: (path = '') => {
  cy.visit(`${appRoute}/${path}`).then(waitForAppToStart);
},

Then in your actual test case in the beforeEach-hook call visitSite()

Related