Cypress.io — Use the Log API with custom commands

This article builds off https://medium.com/@NicholasBoll/cypress-io-scaling-e2e-testing-with-custom-commands-6b72b902aab. Take a few minutes to read that first.

Here’s a recap of the test that chains helpers together.

Recap of testing creating, updating, completing and deleting of a todo

Helper functions are great, but the Command Log still shows a lot more detail than what we see in our test code. It would be better if the logging detail matched our test code 1:1.

Create todo, update it, mark it as done, then delete it. How can you tell?

As you can see from the screenshot, it is hard to tell what Cypress commands matched to our helper abstractions. Applications that are more complex than a todo app may have more interactions per test and matching test code to the logged output becomes more important.

Luckily, Cypress ships with a logging API — the same API Cypress commands use internally. The official documentation and type definitions are still a work in progress, so I’ll outline some key concepts here:

  • name: Name of the command. Ex “createTodo”.
  • message: Contextual information, usually subject or value. Ex: name of the Todo being created.
  • $el: A jQuery-wrapped element for the command (if applicable). When a user hovers over or clicks the command, Cypress will draw a bounding box around this element to highlight it.
  • consoleProps: A function that returns an object. If a command is clicked on in the Command Log, the browser’s console output will contain a list of key-value pairs from the object your function returns. This improves the debugging experience. For our createTodo example, this might be the Todo object that was created.
  • snapshot: One of the most powerful debug features of Cypress. This is a snapshot of the DOM. A command can include more than one snapshot. Some commands like “click” have a “before” and “after” snapshot to see the state of your app before and after a button was clicked. This is similar to “Time travel debugging”.

We will combine these concepts in an update of our helper file to add logging. The Cypress Log API is a bit verbose, so I will just include createTodo.

This is what the Command Log looks like after implementing the Command Log API for the other Todo helpers.

Command Log now matches our helper abstractions

Now we can interact with our custom commands the same way we can interact with built-in commands:

Video of stepping through commands

It takes a bit of playing around to get the command to behave the way you want. For instance, there is a spinning loading indicator for asynchronous commands that starts when Cypress.log is called. This means occasionally using a .then to get the timing right.

Now the Command Log mimics our helper abstractions. I forked the Cypress example: https://github.com/NicholasBoll/cypress-example-todomvc if you want to try it out first-hand. The full helpers using the Log API are here: https://github.com/NicholasBoll/cypress-example-todomvc/blob/master/cypress/helpers/todo_log.ts

There are a few issues to look out for:

  • Sometimes there are phantom snapshots. I haven’t discovered why they happen. An example is shown in the video — update todo has before, 2 and after. The “2” is not a snapshot I specified.
  • The documentation is a work in progress.
  • It is very verbose and error-prone to add { log: false } to all commands. Assertions don’t have a option to disable logging and can look out of place. Also if I need to debug a helper, we’ve obscured the underlying commands from being logged. I’ve been experimenting with using { log: Cypress.env('DEBUG') }. I have a proposal for command grouping — similar to console.group: https://github.com/cypress-io/cypress/issues/1260

Conclusion

The Cypress Log API allows the Command Log to match our custom helpers, increasing the readability and maintainability of our tests.