web-dev-qa-db-fra.com

Affirmez que l'élément n'est pas actionnable dans Cypress

Si un élément n'est pas actionnable sur la page (dans ce cas, couvert par un autre élément) et que vous essayez de cliquer dessus, Cypress affichera une erreur comme celle-ci:

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

<span>...</span>

is being covered by another element:

Génial! Mais existe-t-il un moyen d'affirmer que c'est le cas, c'est-à-dire que l'élément ne peut pas être cliqué?

Cela ne fonctionne pas:

  • should.not.exist - l'élément existe
  • should.be.disabled - l'élément n'est pas désactivé
  • should.not.be.visible - l'élément est visible (juste couvert par un autre élément transparent)
  • en utilisant cy.on('uncaught:exception', ...), car ce n'est pas une exception
16
Laura

Voir les tests Cypress sur 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()

Le plus simple serait d'imiter ce test, même si les documents recommandent de n'utiliser que cy.on('fail') pour le débogage.

Ceci est similaire à un test unitaire utilisant expect().to.throw() pour vérifier qu'une exception se produit comme prévu, donc je pense que le modèle est justifié ici.

Pour être complet, j'inclurais un appel à 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'));
  })

})

En tant que commande personnalisée

Je ne sais pas comment créer l'extension chai que vous avez demandée, mais la logique pourrait être enveloppée dans une commande personnalisée

/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("#button-covered-in-span").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)
});

Remarque

Je m'attendais à ce que done() expire lorsque la prémisse du test (c'est-à-dire que le bouton est couvert) est fausse.

Cela ne se produit pas (raison inconnue), mais en enchaînant .then() hors du 2ème clic permet à done() d'être appelé avec un message d'erreur. Le rappel then() ne sera appelé que si le clic réussit, sinon le rappel cy.once('fail') gère l'échec du clic (selon le propre test de Cypress).

13
Richard Matsen