Cypress DevExtreme Example

Finding and testing DevExtreme components without good test selectors.

Let's say that you do not control the HTML markup on the page. Finding the elements becomes difficult. How do you select the elements following Cypress querying best practices? In this blog post I will show how to interact with DevExtreme widgets.

🎁 You can find the source code for this blog post in the repo bahmutov/cypress-devextreme-example.

📺 Watch the explanation from this blog post in the video "Cypress DevExtreme Example".

I grabbed a FormsAndMultiPurposeOverview example from DevExtreme gallery. The page includes several input widgets, and I would like to pick a different state using the state list popup.

I want to pick a different state

Let's inspect the markup and find how we can click that button. Open the browser DevTools and find the State input widget.

The state input widget

Notice that the actual form value is hidden <input type="hidden" name="State" value="CA">. Let's first verify its initial value, finding the element by its name attribute.

cypress/e2e/spec.cy.js
1
2
3
4
5
6
it('selects the state', () => {
cy.visit('app/index.html')
// by default CA state is selected
cy.get('input[name=State]')
.should('have.value', 'CA')
})

The state field is set to California

Tip: you can find lots of finding elements examples at my cypress-examples querying page.

Now that we found the state input element, we can find the button to open the state selection popup. The "button" (it is a div element with role=button) shares a common ancestor with the input element, thus we can do the following:

1
2
3
4
5
6
7
8
9
it('selects the state', () => {
cy.visit('app/index.html')
// by default CA state is selected
cy.get('input[name=State]')
.should('have.value', 'CA')
.parent()
.find('[aria-label=Select]')
.click()
})

Opening the state selection list

Great, now let's see how we can find the states list. Usually such popups are attached to the document body and use absolute positioning to appear next to the widget they are used with.

The HTML markup for the states list popup

Ughh, nothing terribly specific for our states data - but we can still find this popup.

1
2
3
4
5
6
7
8
9
10
11
12
it('selects the state', () => {
cy.visit('app/index.html')
// by default CA state is selected
cy.get('input[name=State]')
.should('have.value', 'CA')
.parent()
.find('[aria-label=Select]')
.click()
cy.get('[role=listbox]')
.should('be.visible')
.contains('[role=option]', 'MA')
.click()

You can see the test working correctly.

Selecting Massachusetts state from the dropdown

It works, but the popup passes way too quickly, and it might be very difficult to see the list when the video is captured on CI. Thus I like slowing down such steps on purpose.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
it('selects the state', () => {
cy.visit('app/index.html')
// by default CA state is selected
cy.get('input[name=State]')
.should('have.value', 'CA')
.parent()
.find('[aria-label=Select]')
.click()
cy.get('[role=listbox]')
.should('be.visible')
.contains('[role=option]', 'MA')
.scrollIntoView()
.wait(1000, { log: false })
.click()
})

Scrolling the option into the view before clicking

Much better. Finally, we want to confirm that clicking on the popup really changes the <input name="State"> value.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
it('selects the state', () => {
cy.visit('app/index.html')
// by default CA state is selected
cy.get('input[name=State]')
.should('have.value', 'CA')
.parent()
.find('[aria-label=Select]')
.click()
cy.get('[role=listbox]')
.should('be.visible')
.contains('[role=option]', 'MA')
.scrollIntoView()
.wait(1000, { log: false })
.click()
// the input field now has the value "MA"
cy.get('input[name=State]').should('have.value', 'MA')
})

Confirm the state form input has the changed value

See also