cypress-pipe icon indicating copy to clipboard operation
cypress-pipe copied to clipboard

Chaining cy.pipe is dangerous when yield element is removed

Open jogelin opened this issue 2 years ago • 1 comments

Hello,

I am using your library for some time now and I wanted to share about the fact that chaining cy.pipe is bringing some flakiness too.

In your tests, I see that you are testing that if you chain cy.pipe multiple time, the yield element is correctly transferred and the delayed child elements are correctly appended.

I think this is not really representing the reality when we are using a frontend framework that is removing adding dom element dynamically. As you can see on my branch, I am simulating that the yielding element is removed and recreated. In this use case, the test failed.

This is happening a lot when you want for example to access a table, then the first row then a cell, ... if the first row is removed and recreated by the framework because of many reasons, your test is flaky.

I know it can be solved by chaining JQuery utils inside a unique cy.pipe() but I feel the syntax is harder to read.

I was thinking about having a pipe function that allows providing a list of functions instead of only one like this:

const getFirst = $el => $el.find('#first')
const getSecond = $el => $el.find('#second')
const getThird = $el => $el.find('#third')
const getFourth = $el => $el.find('#fourth')
const getFifth = $el => $el.find('#fifth')
const getText = $el => $el.text()

cy.get('body')
  .pipe(getFirst, getSecond, getThird, getFourth, getFifth, getText).should('equal', 'foobar')

Then the entire list can be retried instead of chaining pipe. Having multiple pipe should be avoided IMO

What do you think?

jogelin avatar Nov 10 '21 15:11 jogelin

For the moment, I created a small function like:

function $elPipe(...jQueryFns) {
    return ($startingEl) => jQueryFns.reduce(($el, jQueryFn) => jQueryFn($el), $startingEl);
}

So in the test of my branch:

        // THIS DOES NOT WORK
        // cy.get('body')
        //   .pipe(getFirst)
        //   .pipe(getSecond) // Will resolve after a delay
        //   .pipe(getThird) // Will resolve after a delay
        //   .pipe(getFourth) // Will resolve after a delay
        //   .pipe(getFifth) // Will resolve after a delay
        //   .pipe(getText)
        //   .should('equal', 'foobar')
        
        // THIS WORKS !
        const getFifthText = $elPipe(getFirst, getSecond, getThird, getFourth, getFifth, getText);
        cy.get('body').pipe(getFifthText).should('equal', 'foobar')

jogelin avatar Nov 12 '21 08:11 jogelin