4

I'm trying to do something very similar to this post: https://stackoverflow.com/questions/56145926/how-to-check-if-element-exists-using-cypress-io

I'm using typsecript. Here's my code:

cy.get("body").then($body => {
            if ($body.find(this.timerLocator).length > 0) {
                return cy.get(this.timerLocator);
            }
            else {
                return cy.contains('No Match Found');
            }
});

The problem is that "this.timerLocator" can take up to 60s before appearing. The documentation for find commands mentions that we can pass an option 'timeout'. But when adding a timeout, I receive the following error message:

cy.get("body").then($body => {
            if ($body.find(this.timerLocator, {timeout: 60000}).length > 0) {
                return cy.get(this.timerLocator);
            }
            else {
                return cy.contains('No Match Found');
            }
        });

Expected 1 arguments, but got 2.ts(2554)

I also tried something much easier, like:

try {
    cy.get(this.timerLocator, { timeout: 60000 });            
} catch (error) {
    cy.contains('No Match Found');
}

But this is not working. I never reach the catch bracket.

Can anyone please help me on this one?

EDIT: I actually ended up using a multiple selector and it works great in my case. Here's my code:

const multiSelector = `${this.timerLocator}, :contains("No Match Found")`;
return cy.get(multiSelector, { timeout: 60000 });
NicolasM
  • 77
  • 6
  • If you have to anyways wait 60 seconds before checking if the element exists or not, why not use `cy.wait(60000)` ? – Alapan Das Aug 26 '22 at 22:03
  • No actually, "this.timerLocator" can take up to 60s to appear. Sorry if that was unclear. I'll edit my post. – NicolasM Aug 26 '22 at 22:06
  • The problem occouring here is that you are calling the JQuery .find() instead of the Cypress .find(). Therefore it doesn't accept an options object as 2nd argument. – PhilipAllStar Aug 30 '22 at 14:14

2 Answers2

2

I would avoid defining timeouts and time-based wait(number) at all cost. In the long run they aren't a solution, because they will slow down your tests.

e.g. Most often, you need to wait for a certain API to be called. And in that case, you should define an http interceptor with an alias.

cy.intercept("GET", "/customers*").as("getCustomers");

and then wait for that alias instead.

cy.wait('@getCustomers');

If you're waiting for something else (which is quite exceptional), e.g. some kind of other promise or timeout to be finalized, then find a way to define an alias for that long-running function. e.g. stub it and define an alias for it.

const stub = cy.stub(someServiceToWaitFor, 'functionToWaitFor').as('alias')

You can't stub global functions though. You can only stub functions which are defined on an object. So, that may (or may not) require you to move some code around. (e.g. organize your code in services)

And secondly, it can be challenging to get hold of that service object in your cypress-test code. But you can actually pass variables by defining them on the window object.

  if (window['Cypress']) {
    window['SomeServiceToWaitFor'] = someServiceToWaitFor;
  }

You can then get hold of that object using cy.window... from your test.

bvdb
  • 22,839
  • 10
  • 110
  • 123
2

You can't really do conditional $body.find(...) with a timeout.

Since you have either/or and it looks like they are mutually exclusive, you can use multiple selector

// this selector looks for either timerLocator or element with "No Match Found"
const multiSelector = `${this.timerLocator}, :contains("No Match Found")`;

cy.get(multiSelector, {timeout:60000})
  .then($timerOrNoMatch =>
    ...
  })
Fody
  • 23,754
  • 3
  • 20
  • 37
  • Hi, thanks for your reply. However, get() method does not take 3 arguments. I receive the following error message: Expected 1-2 arguments, but got 3.ts(2554) – NicolasM Aug 30 '22 at 12:26
  • Yes sorry I messed up the selector. It should have a single string as first parameter. This should work: `cy.get(this.timerLocator + ', :contains("No Match Found")', {timeout:60000})` – Fody Aug 30 '22 at 12:57
  • Note the comma in the middle of the string, Cypress will search for 1st part (timerLocator) or 2nd part (any element containing "No MatchFound") and return the first one it finds. – Fody Aug 30 '22 at 13:02
  • It did the trick. I removed the .then() part though because I couldn't return the actual element. Here's my code: `const multiSelector = ``${this.timerLocator}, :contains("No Match Found")``; cy.get(multiSelector, { timeout: 60000 }).then(($timerOrNoMatch) => { return cy.wrap($timerOrNoMatch); })` On my test file, I get the following error message: `Property 'should' does not exist on type 'void'.ts(2339)` – NicolasM Aug 30 '22 at 15:46
  • One last thing @Fody, where did you find these multiple selectors with Cypress ? I don't get why we should use ` or where this : is coming from. Thanks for the help anyway – NicolasM Aug 30 '22 at 16:03
  • Basically Cypress command use jQuery selectors so anything valid for jQuery (see link to jQuery docs) can be applied to Cypress `cy.get()`. – Fody Aug 30 '22 at 20:57