Recently I have conducted a Cypress Level Up Online Workshop April 2023, and this blog post answers some of the questions I have received from the participants. Hope these blog post provides good answers.
- The Todo item selector
- Asserting the number shown
- Confirm text from multiple items
- Confirming the text
📺 You can watch these questions answered in the video Cypress TodoMVC Questions Answered.
The Todo item selector
Q: How do you determine to use the selector ".todo-list li" in the test below?
1 | it ('add 2 items', function(){ |
This is a good question especially considering what the Cypress Selector Playground suggests when you try to pick the first "todo A" item from the list
The Selector Playground is definitely broken when you are trying to pick a list of elements, thus I use my personal judgement. I want to pick all Todo elements on the page. First, I read the Cypress best practices for selecting elements, then I inspect the HTML markup available in my application:
1 | <header>...</header> |
I want to select the Todo items in the list. I won't just use the selector cy.get('li')
since there could be several lists on the page. I want the list of todos, thus I pick the li
elements that are children of the <ul class="todo-list">
element. To select the elements inside a parent element we separate the parent selector .todo-list
from the child item selector li
using the space character, so that gives me .todo-list li
.
This is far from perfect. I would advise keeping the selectors consistent. In .todo-list li
we use the class name "todo-list" and the element name "li". I should have probably used class names for both to keep it consistent:
1 | cy.get('.todo-list .todo') |
I say picking good selectors comes with experience and reading the best practices guides. You can also learn all available ways to pick elements in Cypress by reading my Cypress Querying Examples page.
Asserting the number shown
Q: How do I capture the "1" in "1 item left" and how do I log it? Why does this attempt not work?
1 | // verify filters |
First, let's fix the error. The selector class="todo-count"
in the command cy.get('class="todo-count"')
is incorrect. We could select by the class name as written in the HTML <span class="todo-count">...</span>
using an attribute selector with square brackets [name=value]
. In this case it would be something like cy.get('[class=todo-count]')
. But a much better solution would be to use the .
CSS selector when you want to use the class name:
1 | // verify filters |
Tip: you can find examples of all querying commands and selectors on my Cypress Querying Examples page.
Now the assertion part. When you use cy.get
and other querying commands, you get a jQuery object. Can a jQuery object equal 1
? No. Only a number can equal another number 1. You probably want to confirm that the text inside the found element is "1" (notice the type - everything on the page is a string). You could write the assertion like this:
1 | // verify filters |
Even better is to limit ourselves exactly to the element with the number:
1 | // verify filters |
Confirm text from multiple items
Q: How do I capture both items using one line of code?
1 | - jan |
So we want to get all todos on the page, extract their text content, and confirm these strings are "jan" and "feb". There are a couple of ways to write it. Let's use should(callback)
assertion
1 | cy.get('.todo-list .todo') |
Ok, let's write it in a shorter way, since we want a one-liner. We want to extract text from each element and confirm the array of strings. We can write the following:
1 | cy.get('.todo-list .todo') |
Unfortunately, cy.then
is not querying command, thus we are forced to confirm the length 2 before using cy.then
. Ok, no big deal, we can bring in my cypress-map plugin and really shorten this test:
1 | // https://github.com/bahmutov/cypress-map |
Tip: for more about cypress-map
and Cypress query commands, read my blog post Cypress V12 Is A Big Deal.
Confirming the text
Q: Why does the .and('have.string', 'first item')
fail?
1 | cy.get('.new-todo').type('first item{enter}') |
Cypress comes with Chai.js, Chai-jQuery, and Sinon-Chai assertion libraries. Each assertion tries to confirm the value / type of the subject. The assertion have.string
expects the subject value to be a string, but the subject yielded by the cy.contains
command is a jQuery object
So what do we need to use to confirm the element's text? Look at the assertions on the Assertions example page.
Let's rewrite the test. We can use include.text
assertion
1 | cy.get('.new-todo').type('first item{enter}') |
If you look at the cy.contains command documentation page, notice it always returns just a single item, and that item includes the given text. Thus the same test can be written simply:
1 | cy.get('.new-todo').type('first item{enter}') |