You can watch my video "Cypress Command Chain Plugin Introduction" that covers the contents of this blog post.
The problem
A lot of times, people new to Cypress get an unexpected result while trying to print a value. For example, the following (incorrect) test is supposed to print the number from the page.
1 | // INCORRECT, does not print "n" |
Hmm, why is the n
variable empty?
Cypress command queue
When Cypress runs through the test, it first queues all commands, and then starts executing them. Each command with its arguments is added to the list. It is almost like instructions you might write down and give to a human tester to run later. The above code would be something like this if I ask you to test the page:
- visit the index page
- get the element with id "project-count"
- confirm the text matches the regular expression
/^\d+/
- convert the text to a number
- print the string "number" and
undefined
- START the test
Wait, why is the last instruction says to print "number" and undefined
? Because these are the parameters the test uses when calling cy.log('number', n)
. At the moment of the call, the value of n
is still undefined. It will be set much later.
Visualize the command queue
To better show the queued up commands and make it clear, I have written the cypress-command-chain plugin. Cypress emits events every time a command is queued, started, and ended. You can see these events yourself:
1 | Cypress.on('command:enqueued', (command) => { |
By inspect the command.args
you can see the arguments at the moment cy.log('number', n)
but before the test starts running.
Cypress Command Log shows the current test command and all finished commands. I have written the plugin cypress-command-chain
to show all enqueued commands. This makes it clear which commands are scheduled to run and their arguments. The plugin even warns you if any of the arguments have undefined
value, since it is a sign of a problem.
The fix
From the command queue, we see that we call cy.log('number', n)
too soon. We need to call the log
command after the value of n
is set, which happens inside the .then(...)
callback.
1 | it('prints a number of projects', () => { |
The test goes by too quickly, let's add cy.wait
just to slow it down so we can see the Cypress command queue in action.
1 | it('prints a number of projects', () => { |
Now you can see that the test starts with a queue of commands ending at .then cb()
command. The test runner does not know about the cy.log
command yet. Only when it reaches the .then cb()
callback function, it runs the cy.log
and inserts it into the queue, and then continues executing the commands. When cy.log
is called inside the .then
callback, the value of n
is known, so the command enqueued shows "number", 355
as expected.
Tip: cy.then
command might confuse some people, since it reminds them of JavaScript promises. This is why I suggest renaming it to cy.later for clarity.
Chains
Cypress command queue and fluent syntax make it simpler to "pass the value forward" instead of getting into a variable. Thus the above test could be written as:
1 | it('prints a number of projects', () => { |
The above fluent chain shows nicely
The best approach to writing concise tests like this in my opinion is to see more examples. Read my Cypress blogs and watch the Cypress Tips & Tricks YouTube videos to learn and check out my collection of commands and recipes at glebbahmutov.com/cypress-examples/.