Cypress: Getting or returning elements using within() other elements

Viewed 447

I have methods in my test code that uses within to find and access selectors. Mainly, it's because there are several blocks of code with identically named selectors, which I cannot change. But also because I find it more tidy than dealing with complex and often poorly created indexed selectors/id's.

Simplified markup:

<div data-e2e-selector="byggrad>
  <div data-e2e-selector="adresserad">
    <div data-e2e-selector="etasjerad">
      <div data-e2e-selector="finansieringsobjektrad">
        <input data-e2e-selector="boligbetegnelse">
      </div>
      <div data-e2e-selector="finansieringsobjektrad">
        <input data-e2e-selector="boligbetegnelse">
      </div>
    </div>
    <div data-e2e-selector="etasjerad">
      <div data-e2e-selector="finansieringsobjektrad">
        <input data-e2e-selector="boligbetegnelse">
      </div>
      <div data-e2e-selector="finansieringsobjektrad">
        <input data-e2e-selector="boligbetegnelse">
      </div>
    </div>
  </div>
</div>
  

There may be several elements of each, all nested as above. So, using eq(0) to get the selector with a specific index seems to work fine.

This code works fine:

cy.get('[data-e2e-selector=byggrad]').eq(0)
  .within(() => {
    cy.get('[data-e2e-selector=adresserad]').eq(0)
      .within(() => {
        cy.get('[data-e2e-selector=etasjerad]').eq(0)
          .within(() => {
            cy.get('[data-e2e-selector=finansieringsobjektrad]').eq(0)
              .within(() => {
                cy.get('[data-e2e-selector=boligbetegnelse]').clear();
                cy.get('[data-e2e-selector=boligbetegnelse]').type('TEST');                                     
              });
          });
      });
  });

However, this does not:

this.getBoligbetegnelse().clear();
this.getBoligbetegnelse().type('TEST');

getBoligbetegnelse() {
  return cy.get('[data-e2e-selector=byggrad]').eq(0)
  .within(() => {
    cy.get('[data-e2e-selector=adresserad]').eq(0)
      .within(() => {
        cy.get('[data-e2e-selector=etasjerad]').eq(0)
          .within(() => {
            cy.get('[data-e2e-selector=finansieringsobjektrad]').eq(0)
              .within(() => {
                cy.get('[data-e2e-selector=boligbetegnelse]');                   
              });
          });
      });
  });
}

Of course, I realized after a while, this is because the method returns the [data-e2e-selector=byggrad] element, and not the [data-e2e-selector=boligbetegnelse] element.

So, then I try this:

getBoligbetegnelse() {
  cy.get('[data-e2e-selector=byggrad]').eq(0)
  .within(() => {
    cy.get('[data-e2e-selector=adresserad]').eq(0)
      .within(() => {
        cy.get('[data-e2e-selector=etasjerad]').eq(0)
          .within(() => {
            cy.get('[data-e2e-selector=finansieringsobjektrad]').eq(0)
              .within(() => {
                return cy.get('[data-e2e-selector=boligbetegnelse]');                   
              });
          });
      });
  });
}

But that doesn't work at all, since the method then is not returning anything at all. Of course, because of Cypress' asynchroneous way.

I've played around a bit with .then as well, having read that this is a way to make a command run after the execution has started. But I've found no way to make use of it here.

So, is it possible to find the element this way, and return the inner element instead of the top element? Or do I have to think differently, and start passing values to the methods instead of having them return elements? I'm guessing this is related to the asynchroneous running of Cypress commands and the code?

If this is just how it must be done, it seems to make it hard, if not impossible, impossible to do more effective refactoring, since it's impossible to have ONE method that returns the a code block and then multiple methods that take that code block and look for different specific elements within it.

1 Answers

You might have more success using find() and as() instead of a custom function returning an element. find() and get().within() are fairly similar, but I prefer find() when possible. Difference between find() and get().within()

cy.get('[data-e2e-selector=byggrad]').eq(0)
   .find('[data-e2e-selector=adresserad]').eq(0)
   .find('[data-e2e-selector=etasjerad]').eq(0)
   .find('[data-e2e-selector=finansieringsobjektrad]').eq(0)
   .find('[data-e2e-selector=boligbetegnelse]')
   .as('boligbetegnelse');

cy.get('@boligbetegnelse').then((element) => {
    // code using the element
});
// or
cy.get('@boligbetegnelse').should('be.disabled');

I think another underlying issue might be a misunderstanding between yield and return -- Cypress commands yield their results, allowing them to easily be chainable, whereas you're trying to return the results. It is a slight difference, but a very important one for how Cypress operates! See this SO post on what this means.

Related