Subtitle.
(Cover image from pexels.com by Gu Ko)
- ACT 1: EXPOSITION
- ACT 2: CONFRONTATION
- ACT3: RESOLUTION
ACT 1: EXPOSITION
This is the second part of the article on controlling the execution of your tests in the Cypress and Playwright test frameworks (or Playwright and Cypress, if you prefer 😉). In this second one, we will focus on two features: Tags and Test Filters.
In addition to the out-of-the-box functionality provided by each framework, we will review and evaluate some interesting plugins that address certain gaps related to these features.
As mentioned in the first article The Test Drama: Cypress vs Playwright - Control Your Tests (Part 1): ANNOTATIONS & GROUP TESTS, I recommend reading this second installment in its entirety to gain a comprehensive understanding of these features in significant detail and depth.
ACT 2: CONFRONTATION
Let's explore these tools in detail to see how they offer complete control over our test framework, whether you're using Cypress or Playwright.
🏷️ TAGS
In test frameworks like Cypress and Playwright, tags are used for organizing and managing tests more efficiently. They help in categorizing tests based on certain criteria, such as the type of test, priority, or functionality.
These are some of the common uses:
Test Selection: Tags allow you to run specific subsets of tests based on certain criteria. For instance, you can run only "smoke" tests, "regression" tests, or tests related to a particular feature by specifying the relevant tag.
Filtering: You can filter tests during execution to include or exclude certain tags. This is useful in CI/CD pipelines, where different environments or stages might require different sets of tests.
Organization: Tags help in organizing tests into categories, making it easier to understand the scope and coverage of the test suite.
Reporting: Tags can be used to generate detailed reports that highlight specific areas of the software being tested, which can help in tracking test coverage and identifying gaps.
Overall, effectively using tags can significantly enhance the flexibility, readability, and maintainability of your testing framework. Therefore, we will learn how to use them in both Cypress and Playwright.
CYPRESS TAGS
Tags are not natively supported by the Cypress test framework, which might be surprising but is indeed the case.
To address this gap, there are two grep
plugins you can use in your Cypress framework:
- @cypress/grep: This is the official plugin, featured in the Cypress Plugins list webpage.
- @bahmutov/cy-grep: This plugin is developed by Gleb Bahmutov.
To provide some context: some time ago, Gleb created a grep plugin named cypress-grep
. This plugin was submitted to Cypress.io, which renamed it to @cypress/grep
, making it the official version. However, to introduce improvements, Gleb forked the newly named @cypress/grep
plugin into a separate project called @bahmutov/cy-grep
. He has since maintained his new plugin in the new repository.
Personally, I prefer Gleb's current version of the plugin and use it in my personal and organizational projects for several reasons:
- Gleb updates his plugin very frequently and keeps the dependencies up-to-date, since he relies on it heavily in his projects.
- He resolves issues quickly when reported. For example, I reported a problem with
.only
annotations coexisting with certain configurations just a few days ago, and he fixed it in a matter of hours! - Gleb is the original creator of both plugins and is widely recognized as the Cypress plugin master, a well known fact in the Cypress community.
Although the weekly downloads of the @cypress/grep plugin on npm are approximately ten times greater than those of the @bahmutov/cy-grep plugin, I would like to share additional information from the GitHub repositories of both plugins that supports my opinion as of the date this article was written.
@cypress/grep source code was last updated 2 years ago:
@bahmutov/cy-grep source code was last updated 1 day ago:
@cypress/grep pull requests closed in the past few months (1):
@bahmutov/cy-grep pull requests closed in the past few months (108):
The configuration and use of both grep plugins are essentially the same. Hence, whatever we discuss in this article can be easily adapted for either one.
Another very cool feature of @bahmutov/cy-grep
, which is not supported by the @cypress/grep
plugin, is that when running your tests in 'open' mode (with the browser open), you can perform grep operations directly from the browser's DevTools console using the Cypress.grep()
command.
But wait! There is more...
In "open" mode, you can also run just the failed tests from the DevTools console using Cypress.grepFailed()
.
Alright, it's settled... we'll use the @bahmutov/cy-grep plugin as we move forward in this article. 🤘
Plugin @bahmutov/cy-grep (by Gleb Bahmutov)
You can find this Cypress plugin in:
- npm @bahmutov/cy-grep
- GitHub @bahmutov/cy-grep
You can locate precise instructions on how to install the plugin in your Cypress framework in the plugin's README.md documentation.
It consists of four main steps:
1.Install the plugin as a devDependency in the project root using the terminal:
npm i -D @bahmutov/cy-grep
2.Register the module in the e2e.js
support file by adding the following lines:
// cypress/support/e2e.js
// load and register the grep feature using "require" function
// https://github.com/bahmutov/cy-grep
const registerCypressGrep = require('@bahmutov/cy-grep')
registerCypressGrep()
...
3.Load and register the module in the cypress.config.js
file within setupNodeEvents
:
// cypress.config.js
const { defineConfig } = require("cypress");
module.exports = defineConfig({
...
e2e: {
setupNodeEvents(on, config) {
require('@bahmutov/cy-grep/src/plugin')(config);
// IMPORTANT: return the config object
return config;
},
},
});
4.Configure the environment variables grepFilterSpecs
and grepOmitFiltered
to true
in your cypress.config.js
file (recommended - we will discuss these variables later on):
// cypress.config.js
const { defineConfig } = require("cypress");
module.exports = defineConfig({
env: { grepFilterSpecs: true, grepOmitFiltered: true },
...
});
Tags can be any word representing the nature of the tests (like smoke
). However, it is very common (I would even say recommended) to use the character @
at the beginning of the tag name for clarity (like @smoke
).
Tags can be created at the group test level (describe
or context
) and the test level (it
) by passing an object with a tags
property as the function's second parameter. You can assign either a single tag (as a string) or multiple tags (as an array of strings) to the same test group or test.
This example below illustrates tags at both the suite and test levels, including specifications for either single or multiple tags:
// test-tags.cy.js
// The test suite 'User Registration' has two tags: '@user-reg' and '@smoke'.
describe('User Registration', { tags: ['@user-reg', '@smoke'] }, () => {
// The first test tagged with '@happy' to represent a happy path scenario.
it('should register a new user successfully', { tags: '@happy' }, () => {
// Logic for testing successful user registration.
});
// The second test tagged with '@unhappy' to represent an unhappy path scenario.
it('should not register with an already existing email', { tags: '@unhappy' }, () => {
// Logic for testing registration with a duplicate email.
});
});
Then, you can select which test suites or tests to run using tags by passing them in the environment variable grepTags
when executing the Cypress run command in the CLI: npx cypress run --env grepTags=...
(we will will explore this in detail in the Cypress Test Filters section).
PLAYWRIGHT TAGS
Tags are natively supported by the Playwright test framework. And believe me... this is very very handy! 🤲
A tag in Playwright must start with the character @
. You set tags using the tag
property in the details parameter when defining a test. If you want to specify only one tag, you can provide it as a string. If you would like to tag a test with more than one tag, you can do so by providing an array of string tags.
import { test, expect } from '@playwright/test';
test('Sample test with 1 tag', {
tag: '@smoke',
}, async ({ page }) => {
// ...
});
test('Sample test with 2 tags', {
tag: ['@smoke', '@regression'],
}, async ({ page }) => {
// ...
});
Tags also can be created at the group test level (test.describe
):
// test-tags.spec.ts
import { test, expect } from '@playwright/test';
// The test suite 'Sample group with tag' has one tag: '@query'.
test.describe('Sample group with tag', {
tag: '@query',
}, () => {
// The first test does not have any tag
test('Sample test one', async ({ page }) => {
// ...
});
// The second test has 2 tags '@smoke' and '@regression'
test('Sample test two with 2 tags', {
tag: ['@smoke', '@regression'],
}, async ({ page }) => {
// ...
});
});
And that's it—no more drama or anything else needed! 🙌
Then you can select which tests or group tests to run in the terminal using the --grep
option followed by the tags. We will cover the details in the Playwright Test Filters section.
⚗️ TEST FILTERS
Filtering tests by tags, spec files and test titles allows precise control over which tests to execute. This feature enhances efficiency by enabling QA Engineers to target specific tests relevant to their current work, facilitating faster and more focused testing cycles.
CYPRESS TEST FILTERS
In Cypress, you can natively select which spec files to execute by using the --spec
command when running tests from the terminal.
However, Cypress does not support out-of-the-box filtering tests by tags or test titles. To achieve this, you'll need to use plugins provided by the Cypress community:
Like the previously mentioned @bahmutov/cy-grep, developed by Gleb Bahmutov.
Or the newly released cypress-cli-select, developed by Dennis Bergevin.
Filter by Spec files
Cypress provides built-in support for filtering tests by specifying spec files through the --spec
parameter in the command line.
You can execute specific spec files by specifying their names, or run all tests in a folder that match a glob pattern. It is highly recommended to use double quotes when specifying the files to run, and the path can be either absolute or relative to the current working directory.
To run a specific test file:
$ npx cypress run --spec "cypress/e2e/my-spec.cy.js"
You can specify multiple test files separated by a comma:
$ npx cypress run --spec "cypress/e2e/examples/actions.cy.js,cypress/e2e/examples/files.cy.js"
To run all tests in a folder that match a glob pattern:
$ npx cypress run --spec "cypress/e2e/login/**/*"
You can combine all of this with the --project
parameter, but to be honest, I find it more confusing, so I won’t spend time on it here. If you’d like to see an example of this parameter, refer to the Cypress official documentation.
Filter by Test Title (with @bahmutov/cy-grep)
Cypress does not support filtering by test title out-of-the-box. To accomplish this, you would need to use the cy-grep
plugin.
To filter tests by title, use the --env grep=...
option in the terminal. If the title string you want to filter includes spaces, make sure to enclose it in quotes:
# Run all tests with "footer" in their title
$ npx cypress run --env grep=footer
# Run all tests with "footer links" in their title
$ npx cypress run --env grep="footer links"
Title filters are also applied to test suite blocks (describe
and context
). If a suite matches the filter, all tests within that block will be executed:
# Run any tests in the blocks including "footer"
$ npx cypress run --env grep=footer
describe('test footer', () => {
it('this will run', () => {})
it('this will also run', () => {})
})
Or Substring Matching
You can provide multiple title substrings to match by separating them with a ;
character. Each substring will be trimmed automatically:
# Run all tests with "footer links" or "header links" in their title
$ npx cypress run --env grep="footer links; header links"
Invert Filter
You can invert a filter by placing the -
character in front of the string to match:
# Run all tests WITHOUT "footer links" in their title
$ npx cypress run --env grep="-footer links"
# Run tests with "footer", but without "links" in the titles
$ npx cypress run --env grep="footer; -links"
In "open" mode, you can also run test by title using the command Cypress.grep()
directly in the browser's DevTools console:
// Filter tests by title substring
Cypress.grep('header links')
Additionally, you can run the filtered tests multiple times:
// Run filtered tests 10 times
Cypress.grep('header links', null, 10)
Filter by Tags (with @bahmutov/cy-grep)
As previously mentioned, Cypress does not natively support filtering tests by tags, however this can be achieved using the cy-grep
plugin.
You can choose which tests to run or exclude by using tags with the --env grepTags=...
option. By including commas in the grepTags
environment variable, you can separate multiple tags.
If a specific tag is not found in the specs, then you will get a warning in the terminal:
$ npx cypress run --env grepTags=@wrong-tag
cy-grep: could not find the tag "@wrong-tag" in any of the specs
And Tags
Use the character +
to indicate that both tags must be present in a suite or test for it to run. You can specify the tag string with or without quotes:
# Run suites and tests that have both tags @smoke and @regression
$ npx cypress run --env grepTags="@smoke+@regression"
# This one is equivalent
$ npx cypress run --env grepTags=@smoke+@regression
Or Tags
When you want to run suites or tests that match one tag or another, separate them with a space character. In this case, you must always provide the tag string within quotes because of the spaces:
# Run suites and tests that have the tag @visual or the tag @regression
$ npx cypress run --env grepTags="@visual @regression"
Inverted Tags
You can skip running certain suites and tests that include a specific tag by using the invert option, which involves prefixing the tag with the character -
:
# Do not run any suites or tests that have the tag @quarantine
$ npx cypress run --env grepTags=-@quarantine
# Run suites and tests with the tag @smoke but without the tag @quarantine
$ npx cypress run --env grepTags=@smoke+-@quarantine
Not Tags
You can exclude tests with a specific tag from running, even if they have a tag that should run, by using the "not" option. Simply prefix the tag with --
to skip it:
# Run suites or tests with the tag @visual or @regression but exclude those with the tag @quarantine
# (note that @quarantine is prefixed with -- due to spaces)
$ npx cypress run --env grepTags="@visual @regression --@quarantine"
# This command is equivalent to the previous one
$ npx cypress run --env grepTags='@visual+-@quarantine @regression+-@quarantine'
Grep untagged tests
To run tests without any tags that are also within suites without tags, set the environment variable grepUntagged
to true
when run in the CLI command:
$ npx cypress run --env grepUntagged=true
grepFilterSpecs and grepOmitFiltered
Previously, we recommended setting up these two environment variables when installing the @bahmutov/cy-grep
plugin. Now, let's explore their intended use.
By default, when using the @bahmutov/cy-grep
plugin, all specifications are run, and all internal filters (such as title-based filters) are applied. This process can result in unnecessary time being spent.
To pre-filter specs, you can set the environment variable grepFilterSpecs
to true
. Note that grepFilterSpecs
works only with positive "greps" and is not compatible with inverted ones.
Additionally, the plugin defaults to marking all filtered tests as pending by using it.skip()
. To completely omit these tests from the output, set the environment variable grepOmitFiltered
to true
.
If these environment variables were not set as configuration options during plugin installation, you can specify their values for the run directly in the CLI command:
# Filter all spec files, and run the suites and tests with the tag "@smoke"
# and do not display the filtered tests in the terminal
$ npx cypress run --env grepTags=@smoke,grepFilterSpecs=true,grepOmitFiltered=true
Let's examine what the terminal will display when it encounters the spec file test-groups.cy.js
that finds no suite or test matching the specified tags @user-reg+@smoke
, under various configurations of grepFilterSpecs
and grepOmitFiltered
.
- With the command
npx cypress run --env grepTags=@user-reg+@smoke,grepFilterSpecs=false,grepOmitFiltered=false
, all specifications are executed, filters (such as filters by title) are applied, and any filtered tests are marked as skipped:
- With the command
npx cypress run --env grepTags=@user-reg+@smoke,grepFilterSpecs=true,grepOmitFiltered=false
, the output may look the same; however, specifications are NOT executed (significantly reducing runtime), filters are NOT applied (further saving time), and filtered tests are marked as skipped.
- With the command
npx cypress run --env grepTags=@user-reg+@smoke,grepFilterSpecs=true,grepOmitFiltered=true
, specifications are NOT executed, filters are NOT applied, and filtered tests are omitted from the output:
With this plugin, you can also run tests by tags using the Cypress.grep()
command in the browser's DevTools console when running Cypress in 'open' mode.
// Run filtered tests by tags (with tag @smoke or @regression)
Cypress.grep(null, '@smoke @regression')
And also run by test title, tags and multiple times:
// Run tests with title containing "footer" and tag @regression 10 times
Cypress.grep('footer', '@regression', 10)
⚠️ NOTE: Cypress supports a
--tag
parameter when running tests in the terminal or CI pipeline. However, this--tag
parameter is not used to filter tests by specific tags. Instead, it is used to add tags to the recorded run, making it easier to identify them when displayed in Cypress Cloud.cypress run --record --tag "regression,nightly"`
Plugin cypress-cli-select (by Dennis Bergevin)
Recently, Dennis Bergevin released a plugin called cypress-cli-select, which allows you to interactively select and run specific tests or groups of tests directly from your terminal. It uses the @bahmutov/cy-grep
plugin as its core engine.
To install the plugin, add cypress-cli-select
and @bahmutov/cy-grep
as devDependencies:
$ npm install --save-dev cypress-cli-select
$ npm install --save-dev @bahmutov/cy-grep
Instead of manually typing out spec file paths or using complex command line arguments, cypress-cli-select
visually presents a list of your test files, suites, individual test titles, or tags, enabling you to select which ones to execute.
Just type in the terminal:
$ npx cypress-cli-select run
And "select" you go!
Using the --choose-spec-pattern
option, you can run selected specs in a specific order. However, in this case, you will not have the option to run test titles or tags.
$ npx cypress-cli-select run --choose-spec-pattern
This is a very cool and useful plugin, especially when you are still developing and debugging your tests. Give it a try — it’s worth it!
You can learn everything about this plugin in its documentation. Additionally, there’s an intro video by Gleb that explains how to use it.
PLAYWRIGHT TEST FILTERS
Playwright offers robust native support for filtering tests, allowing you to target specific spec files, utilize tags, or focus on test titles, giving you greater flexibility and precision in test execution.
Filter by Spec files
You can run specific test files in the CLI by simply providing their file names. There's no need to include the full path, as Playwright will search all the directories within the tests
folder that match the specified name.
Additionally, you can execute all test files within specific directories by passing the names of these directories (no need to pass the full path). If the directories contain nested subdirectories, Playwright will recursively include and execute all the tests within them.
# Run two specific test files provided
$ npx playwright test login.spec.ts logout.spec.ts
# Run all tests within the two specified folders
$ npx playwright test tests/contacts/ tests/profile/
If you want to run tests that contain specific keywords in the file name, simply pass those keywords to the CLI.
# Runs tests that contain the keywords 'login' or 'logout'
$ npx playwright test login logout
Filter by Test Title
To execute a test with a specific title, simply use the -g
flag followed by the substring, enclosed in quotes, that must match the test title.
# Runs all the tests that contain the string "validate contacts" in their title
$ npx playwright test -g "validate contacts"
That's it! Quick, easy, and hassle free! 🍰
Filter by Tags
You can run specific test suites (describe
) or tests using the --grep
option followed by the tags. Make sure the tags are enclosed in quotes.
# Runs tests with the tag "@query"
$ npx playwright test --grep "@query"
If a specific tag is not found in the specs, then you will get a warning in the terminal:
$ npx playwright test --grep "@wrong-tag"
Error: No tests found
And Tags
To run suites and tests that include both tags (AND condition), you will need to use regex lookaheads.
Regex lookaheads are a type of zero-width assertion that allow you to check whether a specific pattern exists (positive lookahead) or does not exist (negative lookahead) ahead in the input string without consuming characters. Lookaheads do not form part of the final match; instead, they assert a condition that must or must not be true.
Types of Lookaheads:
-
Positive Lookahead:
(?=...)
Ensures that the specified condition is present ahead in the string.-
Example:
a(?=b)
finds ana
only if it is immediately followed byb
.Match:
"ab"
, No match:"ac"
-
-
Negative Lookahead:
(?!...)
Ensures that the specified condition is not present ahead in the string.-
Example:
a(?!b)
finds ana
only if it is not followed byb
.Match:
"ac"
, No match:"ab"
-
# Runs tests with the tag "@api" and tag "@sanity"
$ npx playwright test --grep "(?=.*@api)(?=.*@sanity)"
Well... if you ask me, I’d have to say that creating a simple AND condition is considerably more complex compared to the cy-grep
plugin we discussed for Cypress. 😒
Or Tags
When you want to run suites and tests that match one tag *OR another, separate the tags using the |
character.
# Runs tests with the tag "@regression" or tag "@sanity"
$ npx playwright test --grep "@regression|@sanity"
Inverted Tags
You can skip running specific suites and tests that include a particular tag by using the --grep-invert
option.
# Skip the tests that includes the tag "@sanity"
$ npx playwright test --grep-invert "@sanity"
You can also filter tests in the configuration file via testConfig.grep and testProject.grep.
Plugin playwright-cli-select (by Dennis Bergevin)
And since Dennis didn’t want Playwright to be left behind, he created a playwright-cli-select plugin (the counterpart of his cypress-cli-select
).
Because Playwright natively supports filtering tests by spec file, test title, and tags, it relies solely on the Playwright core, making its installation even faster than the Cypress version of the plugin.
$ npm install --save-dev playwright-cli-select
To run the plugin, simply:
$ npx playwright-cli-select run
And you get an amazing CLI user interface to select the filters 'on the go':
ACT3: RESOLUTION
Alright, how can I put this in the clearest way possible about what everyone’s been silently thinking as they read through this article?
Why are not tags and filters by title supported in Cypress as part of its core functionality? Why must users rely on a third-party plugin developed by members of the Cypress community to handle such a basic feature?
Both major grep plugins for Cypress were developed by the same individual, Gleb Bahmutov. Notably, the one featured in the official plugin directory hasn't seen updates in quite some time. It raises the question—what would the level of support for this feature be without Gleb's contributions?
I have to admit, tagging and test filtering in Playwright feels far more convenient and user-friendly. Yes, I’m not a huge fan of how conditional AND filtering by test names is handled, but overall, in my opinion, everything else tips the balance heavily in favor of Playwright.
Ok, I said it—someone had to, right?! 🤐
One last thing, though... If you want to rerun the last failed tests, Playwright supports this natively with the --last-failed
flag in the CLI.
$ npx playwright test --last-failed
And what about Cypress? Well, what do you think? 🎲
Bingo! You’ll need a plugin!
For this, you can use:
- The aforementioned
@bahmutov/cy-grep
plugin by Gleb Bahmutov and invoke the commandCypress.grepFailed()
from the DevTools Console.
- Or the cypress-plugin-last-failed created by Dennis Bergevin, which works in both run mode and open mode. It also supports CI/CD pipelines.
If you want to experiment with all the examples discussed in this article, you can clone the repository sclavijosuero/cypress-vs-playwright-frameworks. 🧪 🧫
I'd love to hear from you! Please don't forget to follow me, leave a comment, or a reaction if you found this article useful or insightful. ❤️ 🦄 🤯 🙌 🔥
You can also connect with me on my new YouTube channel: https://www.youtube.com/@SebastianClavijoSuero
If you'd like to support my work, consider buying me a coffee or contributing to a training session, so I can keep learning and sharing cool stuff with all of you.
Thank you for your support!
Wow, what a great article. Love it, Good job @sebastianclavijo ! 👍