Recently I put a challenge to the Cypress users: can you click the "Next" button in the paginated table below until you reach the end of the table?
You can find the introduction to the challenge in the free lesson Lesson n1: Cypress Pagination Challenge of my 🎓 Cypress Plugins course. In this blog post I will post my solutions to the challenge.
🎁 You can find the repo with the code challenge at bahmutov/cypress-pagination-challenge. Clone it, install dependencies, and start solving!
When evaluating a solution, we should think how robust the test is against the web application challenges:
- will the solution work if the table starts with a few items and shows the last page?
- will the solution work if the table takes a little bit of time to transition from one page to the next?
- will the solution work if clicking on the Next button leads to the entire table re-render (including the Next button) or even a full page reload?
- Solution 1: using plain Cypress syntax
- Clean up the Command Log
- Solution 2: using cypress-recurse
- Solution 3: using cypress-if
- Solution 4: using cypress-await
- Solution 5: using cypress-await synchronous mode
- Solution 6: Plain DOM methods
- Coming Soon
- See also
Solution 1: using plain Cypress syntax
We do not know how many times (if any) we need to click the "Next" button, it depends on the size of the table. We know that once the "Next" button has attribute disabled
, we should stop - we reach the end. Thus we use recursion based on the button's attribute.
1 | beforeEach(() => { |
Let's see the solution in action:
📺 You can watch this solution derived in the free lesson Lesson n2: Table pagination solution of my 🎓 Cypress Plugins course.
Of course, we could have used a different syntax to implement the same test. For example, we could get the attribute disabled
using Cypress cy.invoke
command and get the Next button explicitly again after checking if we are on the last page:
1 | function maybeClickNext() { |
The above solutions work correctly when the page starts on the last page.
Clean up the Command Log
Right now our Cypress Command Log looks pretty busy.
Let's clean it up. We can hide the intermediate commands in the recursive loop, limiting the Log to "Page N" and "Last page!" messages. We need to pass the current page number to the recursive function.
1 | function maybeClickNext(page = 1) { |
Tip: cy.invoke command puts the options in the first argument, since the method you are calling might take an unknown number of arguments.
The Log looks so much better now
Solution 2: using cypress-recurse
The above solution is a recursive one. Thus we can write it even simpler using my plugin cypress-recurse.
1 | import { recurse } from 'cypress-recurse' |
The test runs as we expect.
You can find the derivation of the solution in the lesson Lesson n3: Pagination using cypress-recurse which I plan to make public in October.
Solution 3: using cypress-if
We can better represent "if the button is enabled, click on it" using cypress-if plugin.
1 | // https://github.com/bahmutov/cypress-if |
You can find the derivation of the solution in the lesson Lesson n5: Pagination using cypress-if which I plan to make public in October.
Solution 4: using cypress-await
We can write "normal" JavaScript code by adding async / await
support to Cypress using my cypress-await preprocessor. Just set it up in the cypress.config.js
file
1 | const { defineConfig } = require('cypress') |
And use the await
keyword before getting value from the page, like the disabled
attribute
1 | beforeEach(() => { |
You can find the derivation of the solution in the lesson Lesson n6: Paginate using the await keyword which I plan to make public in October.
Solution 5: using cypress-await synchronous mode
Using cypress-await plugin is cool, but writing await cy....
everywhere is noisy. The plugin includes another spec file preprocessor that lets you not write await
in front of every cy
command.
1 | const { defineConfig } = require('cypress') |
Look at the simplicity in this spec
1 | beforeEach(() => { |
Simple and powerful. You can find the derivation of the solution in the lesson Lesson n7: Paginate using synchronous code which I plan to make public in October.
Solution 6: Plain DOM methods
Let's use while
loop and JavaScript DOM methods to interact with the elements on the page. First, we get the button using cy.document command and document.querySelector
. Then we can send mouse event "click" to the button until the DOM element gets disabled.
1 | beforeEach(() => { |
📺 Watch this solution explained in the video Table Pagination Solution Using Plain DOM Methods
Coming Soon
- jQuery +
cy.should
solution - overview of user-submitted solutions vs possible sources of flake
I will post a lesson with each solution derivation on my 🎓 Cypress Plugins course. At first the lesson will be private. After a week, I will make the lesson public and will upload the video to my YouTube channel.