I have been reading the docs about cy.clock
and using it with the component testing setup. But I seem to be doing something wrong here
it.only('shows an estimate on when it is ready (fulfilled)', () => {
const now = new Date()
cy.clock(now)
mount(
<ResizeWrapper>
<CartProvider>
<PaymentSucceededMessage />
</CartProvider>
</ResizeWrapper>
)
// Time left
cy.contains('10 min', { matchCase: false }).should('be.visible')
cy.tick(1000 /*ms*/ * 60 /*sec*/ * 1 /*min*/ + 300 /*ms*/)
cy.contains('9 min', { matchCase: false }).should('be.visible')
// 🧨 Something breaks here 💥
cy.tick(1000 /*ms*/ * 60 /*sec*/ * 1 /*min*/ + 300 /*ms*/)
cy.contains('8 min', { matchCase: false }).should('be.visible') // FIXME: The test does not work but the real life version does
})
the implementation (for now) goes like this
const [minutesLeft, minutesLeftSet] = React.useState<number>(10)
React.useEffect(() => {
let timer
if (minutesLeft > 0) {
timer = setTimeout(() => {
console.log(new Date())
minutesLeftSet((minutesLeft) => minutesLeft - 1)
}, 1000 /*ms*/ * 60 /*sec*/ * 1 /*min*/)
}
return () => {
if (timer) clearTimeout(timer)
}
}, [minutesLeft])
The console only shows 1 print of the new Date()
...?
I think for the same reason you often need to use the callback form of useState setter, e.g minutesLeftSet((minutesLeft) => minutesLeft - 1)
, you also need a beat in the test to allow React hooks to process.
So, this works
const now = new Date()
cy.clock(now)
mount(
<ResizeWrapper>
<CartProvider>
<PaymentSucceededMessage />
</CartProvider>
</ResizeWrapper>
)
// Time left
cy.contains('10 min', { matchCase: false }).should('be.visible')
cy.tick(1000 /*ms*/ * 60 /*sec*/ * 1 /*min*/ + 300 /*ms*/)
cy.wait(0) // yield to React hooks
cy.contains('9 min', { matchCase: false }).should('be.visible')
cy.tick(1000 /*ms*/ * 60 /*sec*/ * 1 /*min*/ + 300 /*ms*/)
cy.wait(0) // yield to React hooks
cy.contains('8 min', { matchCase: false }).should('be.visible')