cypress

cy.task is not performed when it is inside a cy.on('fail') hook


I am wondering if there is a known issue when using cy. commands inside a Cypress.on('fail') hook. I have implemented in my support file the hook at it looks like this:

Cypress.on('fail', (err) => {
    cy.task('sendStat', {host: '0.0.0.0', metric: 'my_metric_failures'});
    throw new Error("UI failure (fail hook)");
})

When having an error at the beginning of a beforeEach function, this hook is called successfully because I see the error in the system output UI failure (fail hook), but for some reason cy.task was totally ignored. Also I tried with a cy.wait and it is totally ignored too! it seems that it ignores any cy. command.

To make things even odder, if the failure comes from a little bit later inside the beforeEach or from inside the it, the task gets called successfully!

I tried to isolate this but I could not reproduce it. I am wondering if you have any idea what it could be. I am using the latest Cypress version (14.1.0)

(I did not have luck at Cypress's github issue site with this question)


Solution

  • Technically, when the fail event is thrown, the Cypress queue runner stops and no more cy commands can be run.

    You may be seeing flaky behaviour with the task running sometimes because the runner does not stop immediately. But generally you should not attempt to run cy.task() inside an event listener like cy.on('fail').

    To make your task run reliably, shift it to an afterEach() hook.


    This is a simple example test with a failing command.

    The afterEach() hook uses function syntax, which gives us access to this.currentTest so we can check if it has failed.

    afterEach(function() {      
      if (this.currentTest.state === 'failed') {
        cy.task('my-task', {host: '0.0.0.0', metric: 'my_metric_failures'})
          .should('eq', 'Return value from task')
      }
    })
    
    it('fails and runs task in afterEach hook', () => {
      cy.visit('https://example.com')
      cy.get('h2')                                // failing here, there is no <h2>
    })
    

    The task I used is simple, it just returns a string so I can assert the task has run.

    on('task', {
      'my-task': (arg) => {
        return 'Return value from task'
      }
    })
    

    enter image description here