Testing Element Attributes with Cypress

Testing Element Attributes with Cypress

When writing end-to-end tests, one common scenario involves verifying that certain elements on a page possess specific attributes or values. Cypress provides various methods to handle such situations efficiently. We will explore how to verify that the title attribute of multiple elements contains one of several expected values. I’ll use an example to demonstrate the problem and present a robust solution.

Problem Description

Consider the following HTML structure:

<div class="wrapper">
<a title="value 1"></a>
<a title="value 2"></a>
<a title="value 3"></a>
</div>

We have three <a> elements within a .wrapper div, each having a title attribute with a distinct value. Our goal is to write a Cypress test that ensures the title attribute of all three elements is one of the expected values: “value 1”, “value 2”, or “value 3”. The challenge is to perform this check to accommodate potential changes in the order of these elements.

Initial Approach

Here’s an initial attempt to solve this problem:

cy.get('.wrapper')
.children().as('socialLinks');

cy.get('@socialLinks')
.should('have.attr', 'title').as('title');

expect('@title').to.contain.oneOf('value 1', 'value 2', 'value 3');

In this approach, we first select the children of the .wrapper and alias them as @socialLinks. Then, we attempt to assert that the title attribute of @socialLinks contains one of the expected values. However, this method does not work as intended because expect cannot be used directly with Cypress aliases in this manner.

Solution Using .each()

Instead of using a for-loop, which can be problematic in Cypress tests, we can leverage the .each() command. This command iterates over each element in a set, allowing us to perform assertions on each element individually. Here’s how we can rewrite the test using .each():

cy.get('.wrapper').children()
.each(($el) => {
const title = $el.attr('title');
expect(title).to.be.oneOf(['value 1', 'value 2', 'value 3']);
});

In this solution, cy.get('.wrapper').children() selects all child elements of the .wrapper. The .each() command iterates over each of these elements. Inside the callback function, we retrieve the title attribute of the current element and use Chai’s expect to assert that it is one of the expected values.

Handling Asynchronous Data Loading

If the values of the title attributes are loaded from an API or if there is any delay in rendering the elements, the previous solution might fail because the assertions are performed immediately. To handle such scenarios, we can replace the expect with a should to ensure that Cypress retries the assertion until it passes or the timeout is reached. Here’s the revised solution:

cy.get('.wrapper').children()
.each(($el) => {
cy.wrap($el).invoke('attr', 'title')
.should('be.oneOf', ['value 1', 'value 2', 'value 3']);
});

In this version, we use cy.wrap($el) to wrap the current element in a Cypress chainable object. Then, we invoke the attr method to retrieve the title attribute and use the should assertion to check that it is one of the expected values. This approach ensures that the test will retry the assertion until the condition is met or the command times out.

Summary

We’ve discussed how to verify that the title attribute of multiple elements contains one of several expected values using Cypress. We started with an initial approach that did not work and moved on to a more robust solution using the .each() command. We also covered how to handle asynchronous data loading by replacing expect with should.

Here are the key takeaways:

  • Use Cypress’s .each() command to iterate over elements and perform assertions on each one.
  • Use cy.wrap() to convert a jQuery element into a Cypress chainable object.
  • Replace expect with should for assertions that might involve asynchronous operations.

Following these guidelines allows you to write more reliable and maintainable Cypress tests for verifying element attributes.

Scroll to Top