Nous écrivons des tests d'interface utilisateur avec cyprès, ce qui est généralement assez simple à utiliser. Mais encore et encore, je tombe sur un problème d'attente fastidieux.
Le scénario est assez simple. L'utilisateur clique sur le bouton de recherche. Il sélectionne ensuite l'un des éléments avec un certain texte. Voici le code:
cy.get('#search-button').click();
cy.contains('Test item 1').click();
cy.get('#cheapest-offer-button').click();
Le troisième événement de clic échoue, car déjà cy.contains('Test item 1')
n'attend pas le rendu de la page et de l'élément. D'après ce que je peux voir dans les étapes de test, il clique simplement au milieu de la page, ce qui ne fait essentiellement rien. Donc, toutes les étapes suivantes échouent bien sûr.
Cependant, si j'ajoute une wait()
entre les appels comme ceci:
cy.get('#search-button').click();
cy.wait(2000);
cy.contains('Test item 1').click();
cy.get('#cheapest-offer-button').click();
La page s'affiche correctement, Test item 1
Apparaît, est cliqué et toutes les étapes suivantes réussissent.
Selon meilleures pratiques l'appel wait()
ne devrait pas être nécessaire et devrait donc être évité. Qu'est-ce que je fais mal ici?
Donnez un délai plus long pour contains
:
cy.get('#search-button').click();
cy.contains('Test item 1', { timeout: 4000 }).click();
cy.get('#cheapest-offer-button').click();
Comme beaucoup de commandes Cypress, contains
ont un second argument qui accepte un objet option. Vous pouvez passer le nombre de millisecondes que la commande doit attendre dans la clé timeout
comme ceci:
.contains('Stuff', { timeout: 5000 }) // Timeout after 5 secs
De cette façon, la commande se comportera comme si vous aviez ajouté un wait
avant, mais si la commande a réussi, elle n'attendra pas tout le temps comme le fait wait
.
Le documents officiels de Cypress sur les délais d'attente explique cette technique: comment cela fonctionne, comment cela doit être fait et comment il affecte les assertions chaînées.
Si la raison pour laquelle vous ne pouvez pas cliquer sur l'élément est la visibilité, vous pouvez essayer .click({ force: true })
, bien que cela ne devrait être qu'un dernier recours car il peut masquer de vrais bogues.
Il semble que ce soit un problème courant https://github.com/cypress-io/cypress/issues/695 .
La solution est de forcer Cypress à attendre toutes les opérations asynchrones comme dans les frameworks basés sur le webdriver Selenium. C'est beaucoup plus rapide que cy.wait () Implémentez la méthode:
function waitForBrowser() {
cy.window().then(win => {
return new Cypress.Promise(resolve => win['requestIdleCallback'](resolve));
});
}
Et utilisez-le comme ceci:
cy.get('#search-button').click();
waitForBrowser();
cy.contains('Test item 1').click();
cy.get('#cheapest-offer-button').click();
Si vous utilisez Angular , mieux vaut utiliser waitForAngular au lieu de waitForBrowser
function waitForAngular() {
return cy.window().then(win => {
return new Cypress.Promise((resolve, reject) => {
let testabilities = win['getAllAngularTestabilities']();
if (!testabilities) {
return reject(new Error('No testabilities. Check Angular API'));
}
let count = testabilities.length;
testabilities.forEach(testability => testability.whenStable(() => {
count--;
if (count !== 0) return;
resolve();
}));
});
});
}
J'ai eu le même problème en essayant d'atteindre une portée avec une certaine valeur. Je l'ai corrigé en fournissant un chemin d'accès plus spécifique à la plage au lieu d'obtenir tous les éléments de la plage dans la page.
cy.get('span').contains('Round ID') //not working
cy.get('.details span').contains('Round ID') //worked
Vous pouvez donc essayer d'être plus précis lors de l'obtention d'éléments du type que vous souhaitez cibler. Une autre option est d'attendre () ...