cypress icon indicating copy to clipboard operation
cypress copied to clipboard

`cy.tick(n)` does not wait for Promises to resolve during the tick; has to be called twice - expose more lolex methods on cy.clock

Open isaaclyman opened this issue 7 years ago • 12 comments

Current behavior:

When a setTimeout callback (or a debounced function, etc.) waits for a promise to resolve and then sets another timeout, you have to call cy.tick(n) twice in order for the second callback to be reached, even if the delay on the tick would be sufficient to cover both timeouts.

Desired behavior:

Cypress should wait for promises to resolve during a tick. Calling cy.tick(n) twice in a row should not be required; it's unintuitive and requires the Cypress tests to have an intimate knowledge of the inner workings of the app they are testing.

If this is not fixable, the problem and workaround should be documented on this page.

How to reproduce:

  • Clone https://github.com/isaaclyman/cypress-test-tiny/tree/double-timeout-test
  • npm install, npm run cypress:open, click the first test file
  • Observe that the first test fails and the second test passes

Test code:

app.js:

var getPromise = () => new Promise((resolve, reject) => {
  setTimeout(resolve, 100)
})

setTimeout(() => {
  getPromise().then(() => setTimeout(() => {
    document.getElementById('main').innerHTML = 'Loaded!'
  }, 100))
}, 100)

spec.js:

describe('page', () => {
  beforeEach(() => {
    cy.clock()
    cy.visit('index.html')    
  })

  // This test fails
  it('loads with a single tick', () => {
    cy.tick(2000)
    cy.get('#main').contains('Loaded').should('exist')
  })

  // This test passes
  it('loads with a double tick', () => {
    cy.tick(1000)
    cy.tick(1000)
    cy.get('#main').contains('Loaded').should('exist')
  })
})

I apologize if this seems contrived. It was causing mysteriously failing tests on an actual project I'm working on, and it took me quite a while to determine the root cause. I don't believe it to be an unrealistic scenario in any complex app built with a modern framework.

  • Operating System: Windows 10
  • Cypress Version: 1.4.2
  • Browser Version: Chrome 64

isaaclyman avatar Feb 07 '18 18:02 isaaclyman

cy.clock and cy.tick are wrappers around lolex: https://github.com/sinonjs/lolex

Lolex has a much broader API that has methods to make this easier. We should probably do a better job exposing all this to you.

Cypress calls into this file to instantiate the clock: https://github.com/cypress-io/cypress/blob/develop/packages/driver/src/cypress/clock.coffee

You likely need the details method to get access to lower level lolex methods like runAll.

cy.clock().then((clock) => {
  const methods = clock.details().methods

  methods.runAll() // haven't tried this, maybe this works
})

brian-mann avatar Feb 09 '18 14:02 brian-mann

or is there a way to use lolex directly in our tests instead of cypress clock ?

bannier avatar Mar 28 '18 12:03 bannier

Sure, you can npm install it and require/import it into your test and use it directly.

chrisbreiding avatar Mar 28 '18 14:03 chrisbreiding

This code does not work btw:

cy.clock().then((clock) => {
  const methods = clock.details().methods

  methods.runAll() // haven't tried this, maybe this works
})

Confirmed the original test case still fails in 3.4.0 of Cypress

jennifer-shehane avatar Jul 23 '19 11:07 jennifer-shehane

I think I'm having a similar problem when using React's useEffect hook which schedules work after render -- the work I'm scheduling is also asynchronous using setTimeout within a Promise.

No matter how many ticks() I add, the timeout never seems to run -- debugging seems to show all the ticks have been 'applied' before the timeout is even created.

tgolden-andplus avatar Oct 09 '19 21:10 tgolden-andplus

Still an issue in version 7 btw.

seb-lean avatar Mar 16 '23 12:03 seb-lean

This issue has not had any activity in 180 days. Cypress evolves quickly and the reported behavior should be tested on the latest version of Cypress to verify the behavior is still occurring. It will be closed in 14 days if no updates are provided.

cypress-app-bot avatar Sep 13 '23 01:09 cypress-app-bot

I am still encountering this issue. My workaround is to make Cypress wait for a millisecond before proceeding with the test.

cy.tick(n);
cy.wait(1);
cy.get('...').should('...');

adamalston avatar Sep 23 '23 01:09 adamalston

This issue has not had any activity in 180 days. Cypress evolves quickly and the reported behavior should be tested on the latest version of Cypress to verify the behavior is still occurring. It will be closed in 14 days if no updates are provided.

cypress-app-bot avatar May 20 '24 01:05 cypress-app-bot

Bump.

adamalston avatar May 22 '24 00:05 adamalston