Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assert that element is not actionable in Cypress

If an element is not actionable on the page (in this case, covered by another element) and you try to click it, Cypress will show an error like this:

CypressError: Timed out retrying: cy.click() failed because this element:

<span>...</span>

is being covered by another element:

Great! But is there any way to assert that this is the case, aka that the element cannot be clicked?

This doesn't work:

  • should.not.exist - the element does exist
  • should.be.disabled - the element is not disabled
  • should.not.be.visible - the element is visible (just covered by another, transparent element)
  • using cy.on('uncaught:exception', ...), since this is not an exception
like image 911
Laura Avatar asked Aug 29 '18 08:08

Laura


People also ask

How do you assert that an element is not present in Cypress?

Use . should('not. exist') to assert that an element does not exist in the DOM.

How do you know if an element is present or not in Cypress?

We can check if these elements exist on the webpage in the following way: cy. get('body') . then($body => { if ($body.

What is force true in Cypress?

In summary, { force: true } skips the checks, and it will always fire the event at the desired element.


1 Answers

See the Cypress tests at click_spec.coffee.

it "throws when a non-descendent element is covering subject", (done) ->

  $btn = $("<button>button covered</button>")
    .attr("id", "button-covered-in-span")
    .prependTo(cy.$$("body"))

  span = $("<span>span on button</span>")
    .css(position: "absolute", 
         left: $btn.offset().left, 
         top: $btn.offset().top, 
         padding: 5, display: "inline-block", 
         backgroundColor: "yellow")
    .prependTo(cy.$$("body"))

  cy.on "fail", (err) =>
    ...
    expect(err.message).to.include "cy.click() failed because this element"
    expect(err.message).to.include "is being covered by another element"
    ...
    done()

  cy.get("#button-covered-in-span").click()

Simplest would be to mimic this test, even though docs recommend only using cy.on('fail') for debugging.

This is similar to a unit test using expect().to.throw() to check that an exception occurs as expected so I feel the pattern is justified here.

To be thorough, I would include a call to click({force: true}).

it('should fail the click() because element is covered', (done) => {

  // Check that click succeeds when forced
  cy.get('button').click({ force: true })

  // Use once() binding for just this fail
  cy.once('fail', (err) => {

    // Capturing the fail event swallows it and lets the test succeed

    // Now look for the expected messages
    expect(err.message).to.include('cy.click() failed because this element');
    expect(err.message).to.include('is being covered by another element');

    done();
  });

  cy.get("#button-covered-in-span").click().then(x => {
    // Only here if click succeeds (so test fails)
    done(new Error('Expected button NOT to be clickable, but click() succeeded'));
  })

})

As a custom command

I'm not sure how to make the chai extension you asked for, but the logic could be wrapped in a custom command

/cypress/support/index.js

Cypress.Commands.add("isNotActionable", function(selector, done) {
  cy.get(selector).click({ force: true })
  cy.once('fail', (err) => {
    expect(err.message).to.include('cy.click() failed because this element');
    expect(err.message).to.include('is being covered by another element');
    done();
  });
  cy.get(selector).click().then(x => {
    done(new Error('Expected element NOT to be clickable, but click() succeeded'));
  })
}) 

/cypress/integration/myTest.spec.js

it('should fail the click() because element is covered', (done) => {
  cy.isNotActionable('button', done)
});

Note

I was expecting done() to time out when the premise of the test (i.e. that the button is covered) is false.

This does not happen (reason unknown), but by chaining .then() off the 2nd click allows done() to be called with an error message. The then() callback will only be called if the click succeeds, otherwise the cy.once('fail') callback handles click failure (as per Cypress' own test).

like image 161
Richard Matsen Avatar answered Sep 18 '22 04:09

Richard Matsen