Skip to content

Commit

Permalink
add recipe with attributes vs props
Browse files Browse the repository at this point in the history
  • Loading branch information
bahmutov committed Jan 5, 2024
1 parent a24e10d commit 72d58d6
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ The Cypress API enables you to configure the behavior of how Cypress works inter
- [Handle telephone links](./recipes/handle-telephone-link.md) like `tel:+1...`
- [Select hidden or visible elements](./recipes/select-hidden-elements.md)
- [Loading element inside an element](./recipes/loader-inside.md) and the accidentally passing test
- [Attributes vs Properties](./recipes/attributes-vs-properties.md)

### Working with the window object

Expand Down
80 changes: 80 additions & 0 deletions docs/recipes/attributes-vs-properties.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Attributes Vs Properties

Sometimes I use `cy.get('...').should('have.attr', '...')` and sometimes I use `cy.get('...').should('have.prop', '...')`. Similarly, sometimes I invoke the jQuery method [attr](https://api.jquery.com/attr/) via `cy.get('...').invoke('attr', '...')` and sometimes I invoke the [prop](https://api.jquery.com/prop/) method like `cy.get('...').invoke('prop', '...')`.

What is the difference?

An HTML element on the page has _attributes_ which are always string values:

<!-- fiddle Attributes vs properties -->

```html
<input id="example" type="checkbox" checked="checked" />
```

The above element has the following HTML attributes: `id`, `type`, and `checked`. We can confirm each declared attribute using the `have.attr` assertion.

```js
cy.get('#example')
.should('have.attr', 'id', 'example')
.and('have.attr', 'type', 'checkbox')
.and('have.attr', 'checked', 'checked')
```

We could get the attribute string value by calling the jQuery `attr` method. Since it yields the attribute value, we need to query the DOM again if we need to confirm more attributes.

```js
cy.log('**invoke attr method**')
cy.get('#example')
.invoke('attr', 'id')
.should('equal', 'example')
cy.get('#example')
.invoke('attr', 'type')
.should('equal', 'checkbox')
cy.get('#example')
.invoke('attr', 'checked')
.should('equal', 'checked')
```

**Tip:** assertion `have.attr` with just the name of the attribute yields the value of the attribute. The above code could be rewritten as

```js
cy.log('**have.attr assertion**')
cy.get('#example')
.should('have.attr', 'id')
.should('equal', 'example')
cy.get('#example')
.should('have.attr', 'type')
.should('equal', 'checkbox')
cy.get('#example')
.should('have.attr', 'checked')
.should('equal', 'checked')
```

The browser reads the HTML and converts each declared HTML element into an object. This object has links to its parent, any children, and _lots_ of other DOM properties. Let's see their names (most of them come from the prototype chain)

![DOM element properties](./pics/element-props.png)

The properties have everything needed by the browser at runtime. For example, the HTML attribute `checked="checked"` becomes the Boolean property `checked`

![Checked element property](./pics/checked-prop.png)

To retrieve the runtime element properties I use the jQuery `prop` method and Chai-Jquery assertion `have.prop`. For example, we can check if the input checkbox element is currently checked:

```js
cy.get('#example').should('have.prop', 'checked', true)
// equivalent
cy.get('#example').invoke('prop', 'checked').should('be.true')
```

**Tip:** I personally like the `have.prop` assertion since it shows the property _name_ in the Command Log, while the `cy.invoke` does not.

![The Command Log difference between the prop assertion and prop call](./pics/prop-name.png)

<!-- fiddle-end -->

## See also

- [`.prop() vs .attr()`](https://stackoverflow.com/questions/5874652/prop-vs-attr)
- [Attributes are equal](./attributes-are-equal.md)
- [Element Attributes](./element-attributes.md)
Binary file added docs/recipes/pics/checked-prop.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/recipes/pics/element-props.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/recipes/pics/prop-name.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 72d58d6

Please sign in to comment.