Resource library

QA How-To

How to Use Playwright tag and grep filters (2026)

Learn Playwright tag and grep filters for smoke, regression, CI, projects, AND and OR selection, exclusions, naming rules, and reliable test reporting.

23 min read | 3,057 words

TL;DR

Add `tag: '@smoke'` or `tag: ['@smoke', '@checkout']` to a Playwright test or describe group, then run `npx playwright test --grep @smoke`. Use `--grep-invert`, regex alternation, or lookaheads for exclusions, OR, and AND logic, and confirm the collected set with `--list`.

Key Takeaways

  • Declare structured tags in the test details object and start every tag with @.
  • Remember that grep matches the complete Playwright test identity, not only the visible test title.
  • Use regex alternation for OR and positive lookaheads for AND selection.
  • Apply stable suite policy in config or projects, and use CLI grep for temporary run selection.
  • Pair grep with grepInvert to express inclusion and safety exclusions clearly.
  • Audit every important filter with --list before trusting it in CI or production smoke runs.
  • Design tags around independent dimensions such as scope, capability, risk, and environment safety.

Playwright tag and grep filters let you classify tests once, then select the right execution set for a pull request, nightly regression, release gate, or production-safe check. Add tags through the test details object, make each tag start with @, and use --grep or configured grep patterns to select matching tests.

The feature is simple at the command line, but reliable suite design requires more than attaching @smoke to a few titles. You need to understand what Playwright matches, how regular-expression logic behaves, where filters belong, and how to prove that a CI command collected exactly the intended tests. This guide builds that model from the API to production use.

TL;DR

Goal Recommended form Example
Tag one test Details object tag: '@smoke'
Tag one test with several labels Tag array tag: ['@smoke', '@checkout']
Tag a group Describe details test.describe('checkout', { tag: '@checkout' }, callback)
Include a tag CLI grep --grep @smoke
Exclude a tag CLI inverted grep --grep-invert @quarantine
Match either tag Regex alternation `--grep '@smoke
Require both tags Positive lookaheads --grep '(?=.*@smoke)(?=.*@checkout)'
Make a permanent lane Project grep grep: /@smoke/

Use structured tags for new code because reports and custom reporters can access them as tags. Before a consequential run, execute the same selection with --list and inspect the names, projects, and total scope.

1. What Are Playwright Tag and Grep Filters?

A tag is classification metadata attached to a test or a describe group. Common examples express execution scope (@smoke, @regression), product capability (@checkout, @search), risk (@critical), or operational safety (@production-safe). The same test can hold multiple tags because those dimensions answer different questions.

grep is Playwright Test's regular-expression filter. Despite the familiar Unix name, it does not scan source files. Playwright first discovers test files and declares test cases, then applies the pattern to each test's complete identity. That searchable string contains the project name, file name, describe titles, test title, and tags, separated by spaces. Therefore /@smoke/ finds a structured @smoke tag, but /checkout/ might match a folder, file, describe block, test title, or project.

There are four main entry points. --grep and its -g alias provide positive CLI selection. --grep-invert excludes matches. Top-level grep and grepInvert create suite-wide policy in playwright.config.ts. Project-level values create named execution lanes with different selection. These mechanisms work with normal title text and tags, although tags create a more deliberate contract.

Filtering changes collection, not the behavior of a running test. A nonmatching test does not start, create fixtures, or appear as a normal passed execution. That makes filters efficient, but it also makes a faulty pattern dangerous: a green job can mean the expected tests passed, or that no valuable tests were selected.

2. Set Up Playwright Test Tags With the Current Syntax

Install and initialize Playwright Test in a TypeScript project if the suite does not exist yet:

npm init playwright@latest
npx playwright test --help

A current test can receive a string tag in the details object between its title and callback. Every tag must begin with @. An array assigns several tags without modifying the human-readable test title.

// tests/login.spec.ts
import { test, expect } from '@playwright/test';

test('member can sign in', {
  tag: ['@smoke', '@auth', '@critical'],
}, async ({ page }) => {
  await page.goto('https://example.com/login');
  await page.getByLabel('Email').fill('member@example.com');
  await page.getByLabel('Password').fill('correct-password');
  await page.getByRole('button', { name: 'Sign in' }).click();
  await expect(page).toHaveURL(/dashboard/);
});

The example is runnable Playwright code, although the URL, account, and locators must represent the application under test. In a real suite, load credentials from a secret store and provision disposable accounts. Do not use tutorial credentials against a live target.

Playwright also recognizes an @ token embedded in a title, such as test('member can sign in @smoke', ...). That form remains supported and can ease migration. Structured tags are preferable for new tests because the title stays focused on behavior and reporter consumers receive explicit tag metadata. Do not write a custom tags() helper or invent a test.tag() call. The supported declaration is the tag property on test or describe details.

3. Add Tags to Tests and Describe Groups

Tag individual tests when classification differs inside one feature. Tag a describe group when every nested test shares a stable attribute. Group tags are inherited by tests declared inside that group, and individual tags can add narrower classification.

// tests/checkout.spec.ts
import { test, expect } from '@playwright/test';

test.describe('checkout', { tag: ['@checkout', '@regression'] }, () => {
  test('shows an empty cart message', { tag: '@smoke' }, async ({ page }) => {
    await page.goto('https://example.com/cart');
    await expect(page.getByText('Your cart is empty')).toBeVisible();
  });

  test('rejects an expired coupon', { tag: '@discounts' }, async ({ page }) => {
    await page.goto('https://example.com/cart');
    await page.getByLabel('Coupon code').fill('EXPIRED');
    await page.getByRole('button', { name: 'Apply' }).click();
    await expect(page.getByRole('alert')).toContainText('expired');
  });
});

The first test is searchable through @checkout, @regression, and @smoke. The second is searchable through @checkout, @regression, and @discounts. Inheritance is useful, but a high-level tag can silently broaden many tests. Review changes to a tagged describe block as selection-policy changes, not mere organization.

Avoid using tags to duplicate every folder and file name. If all files below tests/api/ are already unambiguously API tests and selection is path-based, an @api tag may add little. A tag earns its maintenance cost when it crosses physical organization or represents a policy that reports and CI need.

Annotations are a related but different feature. An annotation holds a type and optional description for issues, requirements, or explanatory report data. Use tags for compact filterable categories. Use annotations for richer context. Do not expect --grep @issue to infer an annotation type unless that exact text is part of the searchable test identity.

4. Understand What Playwright Grep Actually Matches

Playwright applies the regular expression to a combined string made from the project, test file, describe hierarchy, test title, and tags. This explains both the power and the surprising false positives of broad patterns. Suppose a test has this identity:

chromium checkout/cart.spec.ts checkout guest sees cart @smoke @checkout

--grep @smoke is specific because @smoke is an intentional token. --grep checkout is broad because it can match the file path, describe title, or tag. --grep chromium selects by project identity as well as any title that contains that word. Prefer --project=chromium when project selection is the goal because it communicates intent and supports project wildcards.

Regular-expression metacharacters retain their meaning. A pipe means OR. Parentheses group expressions. . means any character unless escaped. .* means any sequence. Lookaheads can require multiple tokens. Shell parsing happens before Playwright sees the pattern, so quote expressions containing spaces, pipes, parentheses, asterisks, or exclamation-sensitive syntax. Single quotes are convenient in Bash and zsh, while PowerShell and Windows command shells have different quoting rules.

Tags should use a conservative character set, for example lowercase letters, digits, and hyphens after @. Names such as @production-safe and @payments are readable and regex-friendly. Avoid tag values containing spaces, regex punctuation, changing ticket numbers, or owner names that will become stale. Test the exact command in the shell used by CI. A regex that is correct in TypeScript can still be altered by YAML or shell quoting.

5. Run Playwright Tag and Grep Filters From the CLI

The CLI is the best place for temporary developer focus and pipeline parameters. Start with a simple positive filter and use --list to preview selection:

npx playwright test --grep @smoke --list
npx playwright test --grep @smoke
npx playwright test -g @checkout

Use inverted grep to remove a known class from an otherwise broad run:

npx playwright test --grep-invert @quarantine
npx playwright test --grep @regression --grep-invert @destructive

The second command expresses a useful two-stage policy: include regression tests, then exclude destructive tests. It is easier to review than a single negative lookahead and allows CI variables to control the positive and negative dimensions independently.

File filters can be combined with grep. Arguments after playwright test are regular expressions matched against full test file paths, while --grep filters the test identity. The command below narrows discovery to checkout files and then selects their smoke tests:

npx playwright test tests/checkout --grep @smoke --project=chromium

If a test unexpectedly disappears, remove constraints one at a time: first project, then grep invert, then positive grep, then path selection. Run each stage with --list. If a test unexpectedly appears, inspect every part of its identity, including inherited group tags and the project name. UI mode can help a developer explore tests, but the text list remains an excellent CI audit artifact because it reflects the exact noninteractive command.

6. Configure Grep Globally and Per Project

Stable selection policy belongs in playwright.config.ts. A top-level filter applies to the configured suite. Project filters let one codebase expose named lanes such as pull-request smoke, full regression, or production-safe monitoring.

// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  grepInvert: /@quarantine/,
  use: {
    baseURL: 'https://example.com',
    trace: 'on-first-retry',
  },
  projects: [
    {
      name: 'pr-smoke',
      use: { ...devices['Desktop Chrome'] },
      grep: /@smoke/,
    },
    {
      name: 'chromium-regression',
      use: { ...devices['Desktop Chrome'] },
      grep: /@regression/,
    },
    {
      name: 'production-safe',
      use: { ...devices['Desktop Chrome'] },
      grep: /@production-safe/,
      grepInvert: /@destructive/,
      workers: 1,
    },
  ],
});

Top-level and project settings should be reviewed together. A global exclusion is useful for quarantine policy, but it can surprise a developer who explicitly selects a quarantined tag and still gets no execution. Document permanent exclusions near the commands that teams use. If an investigation must run quarantined tests, provide a dedicated config or project rather than encouraging arbitrary edits to shared policy.

Project filters make reports self-describing and can pair selection with browser, base URL, retries, or worker limits. They can also duplicate tests when several projects match the same tag. That may be intentional cross-browser coverage, but it must be visible in runtime planning. See Playwright projects configuration patterns for a deeper project matrix design.

7. Express OR, AND, and Exclusion Logic Safely

Selection logic is regular-expression logic. For either @smoke or @sanity, use alternation:

npx playwright test --grep '@smoke|@sanity'

For both @smoke and @checkout, use positive lookaheads. Each lookahead checks the complete test identity without consuming it:

npx playwright test --grep '(?=.*@smoke)(?=.*@checkout)'

For checkout tests that are either smoke or critical, keep the required capability and group the alternatives conceptually:

npx playwright test --grep '(?=.*@checkout)(?=.*@smoke|.*@critical)'

When exclusion is a separate policy, prefer --grep-invert over embedding a negative lookahead:

npx playwright test --grep '@checkout' --grep-invert '@quarantine|@destructive'

In configuration, grep and grepInvert accept a RegExp or an array of regular expressions. Multiple positive patterns in an array are alternatives, not a hidden AND operator. Use one lookahead expression when all labels are required. Write focused tests for complex selection conventions, or at minimum retain reviewed --list output in pipeline diagnostics.

Do not make a regex more clever than the team can maintain. If a command has four lookaheads, several exclusions, and path assumptions, the taxonomy is probably doing too much. A named Playwright project with a clear tag contract can move that complexity into typed, reviewed configuration.

8. Design a Scalable Tag Naming Convention

A useful taxonomy separates dimensions instead of treating tags as an unstructured label cloud. Each test should have only the tags that affect a real selection or reporting decision. A practical model is shown below.

Dimension Examples Decision it supports
Execution scope @smoke, @regression How much confidence is needed now?
Capability @auth, @checkout, @search Which product area changed?
Risk @critical, @security-sensitive Which failures block release?
Environment safety @production-safe, @destructive Where may this test run?
Test type @api, @visual, @accessibility Which runner or review workflow applies?
Temporary status @quarantine Is normal gating temporarily disabled?

Define semantics, not just spelling. @smoke might mean a fast, deterministic, business-critical check with isolated data. If one team uses it to mean quick and another uses it to mean important, the tag cannot support a reliable time budget or release gate. Store the definition in the test repository and assign an owner to temporary states.

Avoid using priorities such as @p0 without defining whether they refer to product impact, test schedule, or defect severity. Avoid person or team tags that change during reorganization. Prefer capabilities that remain recognizable. Enforce lowercase spelling in code review or a small static check, because @Smoke and @smoke are different regex text.

A test may legitimately be @smoke, @checkout, @critical, and @production-safe. Those labels compose because each answers a different question. Duplicate labels such as @smoke, @sanity, and @quick on the same test usually signal unclear scope definitions.

9. Build CI Lanes With Tags Without Hiding Coverage

CI should call named commands whose selected scope can be audited. Package scripts provide a readable boundary:

{
  "scripts": {
    "test:e2e:smoke": "playwright test --grep @smoke",
    "test:e2e:regression": "playwright test --grep @regression --grep-invert @quarantine",
    "test:e2e:production": "playwright test --project=production-safe"
  }
}

A pull request can run smoke tests for fast feedback, while a scheduled job runs regression. A production job should use a dedicated target, minimal credentials, safe data, and an allow-list tag such as @production-safe. Never assume an inverted @destructive filter alone proves safety. Untagged destructive tests would pass that exclusion. Positive approval is the safer boundary.

Add a collection sanity check for critical lanes. npx playwright test --project=pr-smoke --list lets reviewers see scope, but automation can also detect an empty or unexpectedly tiny list using repository-specific expectations. Avoid a brittle fixed count when tests change frequently. Better checks validate known sentinel journeys, required capabilities, or a reasonable range maintained with the suite.

When distributing runs, tags select behavior while shards divide the selected executions across jobs. Keep those jobs conceptually separate. Review Playwright sharding and browser grid tradeoffs before combining project matrices, grep filters, and shards. Preserve blob or HTML reports so repeated project executions remain distinguishable.

10. Debug Filtering and Use Reports as Evidence

Start every filtering investigation with the resolved list. Use the same config, environment variables, project, file argument, and grep pattern as the failing pipeline. Local success with a different shell or project is not equivalent evidence.

npx playwright test --config=playwright.config.ts --project=pr-smoke --list
npx playwright test --project=pr-smoke --grep @checkout --list
npx playwright test --project=pr-smoke --grep @checkout --reporter=line

Look for inherited describe tags, title tokens, broad file names, global grepInvert, and project-level filters. If the CLI adds --grep, verify the resolved interaction experimentally rather than assuming it replaces every configured constraint. Keep permanent selection in one obvious layer where possible.

Structured tags appear in Playwright reporting data and are available to custom reporters through the test case tag collection. That enables dashboards by capability or scope without parsing title strings. Tags are not runtime fixtures, however. Do not branch application behavior because a tag exists. Configuration and fixtures should supply environment or data variants, while tags classify the test.

A filter is correct only when inclusion and exclusion are both tested. Pick known examples that must run and known examples that must not run. For production-safe selection, include a deliberately destructive sentinel in a nonproduction validation project and assert that the production list omits it. For quarantine, report excluded tests separately so temporary removal from gating does not become permanent invisibility. Playwright HTML report troubleshooting can help teams retain useful execution evidence.

11. Review Playwright Tag and Grep Filters Before Release

Use a short review checklist whenever a tag or filter changes:

  1. Confirm every structured tag begins with @ and follows the repository's lowercase naming rules.
  2. State which pipeline, report, or developer workflow consumes the tag.
  3. Run the intended positive command with --list.
  4. Run at least one negative case that must be excluded.
  5. Check inherited describe tags and all matching projects.
  6. Verify shell quoting in the actual CI operating system.
  7. Confirm production selection uses a positive safety allow list.
  8. Give temporary tags such as @quarantine an owner and removal condition.

Also review cost. A single test with @smoke may execute in several browser projects. Adding @regression may include it in a second scheduled job. That is not necessarily duplication, but the team should understand the resulting executions. Use report project names and CI summaries to make repeated coverage explicit.

Finally, test taxonomy should evolve through controlled changes. Rename tags repository-wide, update config and scripts in the same change, and compare old and new --list output. Leaving aliases indefinitely creates ambiguity. If a capability tag no longer influences selection or reporting, remove it rather than preserving decorative metadata.

Interview Questions and Answers

Q: What does Playwright grep match?

It applies a regular expression to the full test identity, including project name, file name, describe titles, test title, and tags. This is why a plain word can match more than the visible title. I use @-prefixed structured tags for deliberate suite selection and verify the result with --list.

Q: How do you run tests that have both @smoke and @checkout?

Use positive lookaheads: --grep '(?=.*@smoke)(?=.*@checkout)'. Each lookahead requires one token somewhere in the searchable identity. I quote the expression so the shell passes it unchanged.

Q: What is the difference between grep and grepInvert?

grep keeps matching tests, while grepInvert removes matching tests. A clear regression lane might include @regression and invert @quarantine. For production safety, I still prefer an explicit @production-safe allow list instead of relying only on exclusions.

Q: Should tags be placed in the title or details object?

Both forms can be filtered, but I prefer the tag field in the details object. It keeps the behavior title readable and exposes explicit tags to reports. Title tokens are mainly useful during migration or in a repository with an established convention.

Q: When should grep be in config instead of the command line?

Permanent suite policy and named lanes belong in config or projects. Temporary developer focus and simple pipeline parameters fit the CLI. I avoid splitting one complex policy across several layers because debugging the effective selection becomes harder.

Q: How do you prevent a filtered CI job from passing with no useful tests?

I preview collection with --list and add a repository-specific scope guard for important lanes. That guard can require sentinel journeys or capability coverage instead of depending on an exact test count. Reports must also show which project and selection ran.

Q: Can tags control fixtures or browser configuration?

Tags classify and filter tests. Projects, use options, and fixtures configure execution. If behavior needs a specific tenant or browser context, I model that through a project or typed fixture rather than reading a tag and branching during the test.

Common Mistakes

  • Writing a nonexistent test.tag() helper instead of the supported tag details property.
  • Forgetting the required @ prefix on structured tags.
  • Assuming grep searches only the visible test title.
  • Using an unquoted pipe or parentheses and letting the shell alter the regex.
  • Treating arrays of configured regular expressions as logical AND.
  • Relying only on --grep-invert @destructive for production safety.
  • Adding broad tags to describe groups without reviewing every inherited test.
  • Keeping @quarantine forever without ownership, diagnosis, or removal criteria.
  • Combining path, project, grep, and invert filters without validating the final list.
  • Creating synonyms such as @smoke, @sanity, and @quick with no distinct meaning.
  • Reporting a green filtered job without showing selected scope.

Conclusion

Playwright tag and grep filters are dependable when tags express stable, documented dimensions and regex selection stays understandable. Declare tags in test or describe details, use grep for inclusion, use grepInvert for explicit exclusions, and keep lasting execution lanes in named configuration.

Choose one real workflow now, such as pull-request smoke or production-safe monitoring. Define its tag contract, tag a small representative set, inspect the exact --list output, and only then make the filtered command a release signal.

Interview Questions and Answers

Explain Playwright tag and grep filters in one minute.

Tags are `@`-prefixed metadata declared on a test or describe group. Grep uses a regular expression to select against the full test identity, including project, path, suite titles, test title, and tags. I use CLI grep for temporary selection, project grep for named lanes, grepInvert for exclusions, and `--list` to audit scope.

How would you implement AND and OR tag selection?

For OR, I use regex alternation such as `@smoke|@sanity`. For AND, I use positive lookaheads such as `(?=.*@smoke)(?=.*@checkout)`. I quote the expression for the target shell and validate known included and excluded cases.

How would you design tags for a large Playwright suite?

I separate dimensions: execution scope, capability, risk, environment safety, and test type. Every tag must support a real selection or reporting decision and have documented semantics. Temporary states such as quarantine also need an owner and exit condition.

Why is an allow-list tag important for production tests?

An exclusion only removes tests already known to be unsafe. An untagged destructive test could still run. A reviewed `@production-safe` tag creates positive approval, which I pair with least-privilege credentials, safe data, and usually a defensive exclusion.

What would you check when grep returns zero tests?

I reproduce the exact CI command with `--list`, then remove project, invert, positive grep, and path constraints one at a time. I inspect global and project config, inherited tags, regex case, and shell quoting. I also ensure the correct config and test directory were loaded.

Should a test branch on its tags at runtime?

No. Tags are classification and collection metadata. Projects and fixtures should provide browser, environment, account, or data behavior so tests stay explicit and typed. Runtime branching on labels makes selection and execution responsibilities hard to reason about.

How do filtered runs interact with projects and sharding?

Projects create configured executions, grep narrows eligible tests, and sharding divides the resulting executions across jobs. A test can run more than once if several selected projects match it. Reports should retain project identity so that duplication is intentional and traceable.

How do you keep quarantine from hiding failures permanently?

I exclude quarantine from gating but run it in a visible scheduled lane. Each quarantined test links to tracked work, has an owner and removal condition, and remains present in reports. The team reviews quarantine age and root cause instead of treating the tag as a permanent status.

Frequently Asked Questions

How do I add a tag to a Playwright test?

Pass a details object between the title and callback, for example `{ tag: '@smoke' }`. For several labels, use an array such as `{ tag: ['@smoke', '@checkout'] }`, and ensure every tag starts with `@`.

How do I run a specific tag in Playwright?

Run `npx playwright test --grep @smoke`. Add `--list` first to preview the selected tests, especially when config or project filters also apply.

How do I grep multiple Playwright tags?

Use alternation for either tag, such as `--grep '@smoke|@sanity'`. Use positive lookaheads for both tags, such as `--grep '(?=.*@smoke)(?=.*@checkout)'`.

What is Playwright grep invert?

`--grep-invert` excludes tests whose complete identity matches the provided regular expression. The equivalent configuration property is `grepInvert`, available globally and per project.

Can I tag a Playwright describe block?

Yes. Pass a details object to `test.describe`, for example `test.describe('checkout', { tag: '@checkout' }, () => {})`. Nested tests inherit the group tag and may add their own tags.

Why does Playwright grep run an unexpected test?

Grep matches the project name, file name, describe titles, test title, and tags as one string. A broad word may match any of those parts, so inspect the full listed identity and use an `@`-prefixed tag for precise classification.

Can Playwright grep be configured per project?

Yes. Add `grep` or `grepInvert` beside `name` and `use` in a project entry. This is useful for named smoke, regression, or production-safe execution profiles.

Are Playwright tags available in reports?

Yes. Structured tags are displayed in Playwright reports and are exposed to custom reporters through test case tag metadata. This supports reporting by scope or capability without parsing title suffixes.

Related Guides