2

I'm testing React Application that pre rendered with Next.js. My application have two pass rendering: loading screen and actual app screen. So I wrote cypress test like this:

// using @testing-library/cypress
it('Some of my tests', () => {
  cy.visit('http://localhost:4000/annotation/edit');
  // bypass next.js router alert outside of my app
  cy.get('div#__next').findByRole('alert', { name: /loading/i });
  cy.findByRole('form', { name: /sign in form/i }).within(() => {
    cy.findByRole('textbox', { name: /account name/i }).type('...');
    cy.findByRole('textbox', { name: /email/i }).type('...');
    cy.findByLabelText(/password/i).type('...');
    cy.findByRole('button', { name: /sign in/i }).click();
  });
  cy.findByRole('progressbar');
});

But it sometimes success, sometimes fail by skipping the first screen. I checked with cypress video and browser runner, there was loading screen and transition to actual app. But Cypress couldn't capture it. The alert in loading screen has accesible name /loading is not conditional, always same. I think Cypress is unstable at rendering.

Is there any way to make a throttle in some steps or capture current DOM?

BaHwan Han
  • 347
  • 1
  • 12

1 Answers1

1

Correct me if I'm wrong, you are adopting this pattern of code to give the user a better experience during loading

const [hasMounted, setHasMounted] = React.useState(false);

React.useEffect(() => {
  setHasMounted(true);
}, []);

if (!hasMounted) {
  return <Alert>Loading</Alert>  // compiled on the server
}

return <SignInForm>

Seems the flakyness is because the window of time for the loading alert is very small.

One way to handle it might be to hand control of setHasMounted to Cypress.

const [hasMounted, setHasMounted] = React.useState(false);

React.useEffect(() => {
  if (window && window.Cypress) {
    window.Cypress.setHasMounted = setHasMounted   // now the test can call this
  } else {
    setHasMounted(true);
  }
}, []);

if (!hasMounted) {
  return <Alert>Loading</Alert>  // compiled on the server
}

return <SignInForm>

Test

cy.visit('http://localhost:4000/annotation/edit');

cy.get('div#__next')
  .findByRole('alert', { name: /loading/i })
  .then(() => Cypress.setHasMounted(true))

cy.findByRole('form'...
Paolo
  • 3,530
  • 7
  • 21
  • You understood correct. I wish there's better solution that decouple my app with Cypress... isn't this possible? – BaHwan Han Sep 03 '21 at 04:51
  • It think it can also be done if you `setTimeout(() => setHasMounted(true), 10)` in the `useEffect()`, then Cypress `cy.clock()` & `cy.tick(10)` can control the timeout. But it's dubious if that is a better way – Paolo Sep 03 '21 at 04:55