reactjscypresscypress-clock

cy.clock and cy.tick not working with split code


Let's assume I have in my React.js project a setInterval which is a countdown from 15 to 0 seconds. However I want to make this process faster in the e2e tests, as 15 seconds could be 60, 120 or even 240 seconds.

So, this code works:

describe('Test timer', () => {

  before(() => {
    cy.clock()
    cy.visit('http://localhost:3000')
  })

  it('Displays timer, pass 15 seconds, does not display timer', () => {
    cy.get('div [data-cy=counterText]').should('have.text', '15');
    cy.tick(15000);
    cy.get('div [data-cy=counterText]').should('have.text', '0');
  })
})

However in projects I usually split some code like this:

describe('Test timer', () => {

  before(() => {
    cy.clock()
  })

  it('Visit site', () => {
    cy.visit('http://localhost:3000')
  })

  it('displays timer', () => {
    cy.get('div [data-cy=counterText]').should('have.text', '15');
  })

  it('does not display timer', () => {
    cy.tick(15000);
    cy.get('div [data-cy=counterText]').should('have.text', '0');
  })
})

Problem is, the code above gives me the following error message:

cy.tick() cannot be called without first calling cy.clock()

I tried to move cy.clock() to different places already but no success, so the question is: where should I place cy.clock() in the use case above?

I'm using Cypress 8.X, just in case.

Thanks in advance!


Solution

  • You have to preserve the clock instance, as (I think) the cleardown between tests is affecting the pattern you are expecting.

    You also need to turn off logging on the tick, as there's a bug within the logging code. (didn't dive too deep on that).

    describe('Test timer', () => {
    
      let clock
      before(() => {
        cy.clock().then(c => clock = c)
      })
    
      it('Visit site', () => {
        cy.visit('http://localhost:3000')
      })
    
      it('displays timer', () => {
        cy.get('div [data-cy=counterText]').should('have.text', '15');
      })
    
      it('does not display timer', () => {
        clock.tick(15000, {log: false});
        cy.get('div [data-cy=counterText]').should('have.text', '0');
      })
    })
    

    Simple app for testing

    <div>
      <div id="counter" data-cy="counterText">15</div>
    </div>
    <script>
      let intervalID = setInterval(() => {
        const div = document.getElementById('counter')
        const newText = +div.innerText - 1
        console.log(`setting ${newText}`)
        div.innerText = newText
        if (newText < 1) {
          clearInterval(intervalID)
        }
      }, 1000)
    </script>