0

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 Answers1

2

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.

agoff
  • 5,818
  • 1
  • 7
  • 20
  • 1
    Well. I know that Cypress records the whole command queue BEFORE executing the actual test (asynchroneously), so that pretty much makes all normal functionality related to variables etc. impossible. This is another argument that Cypress is created for frontend delelopers, for creating what is basically frontend unit tests (mocking for efficienvy). It is NOT created as an end-to-end testing technology. End-to-end needs the ability to persist data to/from the pages of an application, which is not sensibly possible in any way with Cypress. –  Apr 20 '21 at 15:08
  • 1
    If I answered your question and solved your issue, please mark my answer as such. I don't work on Cypress, and can't really speak much to their philosophy on why they made certain decisions. I would only challenge that just because something is not how you are used to means that isn't sensible/possible/etc. Learning new languages and technologies and patterns can expand your knowledge of how to solve problems, and that's a terrific way to grow! – agoff Apr 20 '21 at 15:38
  • Using find() like that creates not very pretty code IMO. I use page classes which return Cypress.Chainable elements, hiding code like '[data-e2e-selector=adresserad]' from the test itself. (Not shown in my question, since I simplified it.)That's not possible using find(). –  Apr 20 '21 at 15:43
  • UPDATE: No, it dosn't answer the question. It just shows a slightly different way to do the exact same thing. Thanks for trying, though. –  Apr 20 '21 at 15:50
  • 1
    Hmm.. I think I'm confused then. What exactly is your issue and what are you trying to solve? It sounds like your initial concern was that you couldn't find the element. If your main concern is that this doesn't play nicely with a POM, I can edit my answer to use a POM to show you how that'd operate. (My other suggestion is to not use POM with Cypress, but use app actions -- https://www.cypress.io/blog/2019/01/03/stop-using-page-objects-and-start-using-app-actions/ ) – agoff Apr 20 '21 at 15:56