Group, Tag and Filter Playwright Tests (describe, Tags and grep)

As a test suite grows, you need to run the right tests at the right time. In this lesson you will group tests with describe blocks, label them with tags, filter runs with grep, and make those filters permanent in the config and npm scripts.

Advertisement

๐Ÿ“– What You’ll Learn: test.describe and nested groups, the tag option and title tags, a practical tagging strategy, –grep and –grep-invert including AND and OR patterns, grep in config projects, npm scripts, describe.configure and serial mode, focus and skip, and reading tags at runtime.


๐Ÿ—‚๏ธ Why Organize Tests?

With ten tests, organization does not matter. With five hundred, it decides whether your team can answer questions like:

  • “Run only the smoke tests before every deployment.”
  • “Run the checkout tests after the payments team changed something.”
  • “Skip the slow tests on pull requests, run them nightly.”

Playwright gives you three tools for this: describe blocks to group tests, tags to label them, and grep to filter what runs.


๐Ÿ“ฆ Group Tests with test.describe

test.describe groups related tests under a title. Groups can be nested, and the titles are combined in reports, for example Cart โ€บ Add items โ€บ add one product.

import { test, expect } from '@playwright/test';

test.describe('Cart', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('https://www.saucedemo.com/');
    await page.getByPlaceholder('Username').fill('standard_user');
    await page.getByPlaceholder('Password').fill('secret_sauce');
    await page.getByRole('button', { name: 'Login' }).click();
  });

  test.describe('Add items', () => {
    test('add one product', async ({ page }) => {
      await page.locator('[data-test="add-to-cart-sauce-labs-backpack"]').click();
      await expect(page.locator('.shopping_cart_badge')).toHaveText('1');
    });

    test('add two products', async ({ page }) => {
      await page.locator('[data-test="add-to-cart-sauce-labs-backpack"]').click();
      await page.locator('[data-test="add-to-cart-sauce-labs-bike-light"]').click();
      await expect(page.locator('.shopping_cart_badge')).toHaveText('2');
    });
  });

  test.describe('Remove items', () => {
    test('remove a product', async ({ page }) => {
      await page.locator('[data-test="add-to-cart-sauce-labs-backpack"]').click();
      await page.locator('[data-test="remove-sauce-labs-backpack"]').click();
      await expect(page.locator('.shopping_cart_badge')).toHaveCount(0);
    });
  });
});

Describe blocks also scope hooks, so each group can have its own setup.


๐Ÿท๏ธ Tag Tests

A tag is a label that starts with @. Add tags with the tag option (recommended) or directly in the title:

// Option 1: tag details (shown as tags in the HTML report)
test('user can log in', { tag: '@smoke' }, async ({ page }) => {
  // ...
});

test('full checkout flow', { tag: ['@regression', '@checkout'] }, async ({ page }) => {
  // ...
});

// Option 2: tag in the title
test('user can log out @smoke', async ({ page }) => {
  // ...
});

// Tag a whole group
test.describe('Payments', { tag: '@payments' }, () => {
  test('pay by card', async ({ page }) => { /* ... */ });
  test('pay by UPI', async ({ page }) => { /* ... */ });
});

A practical tagging strategy

TagMeaningWhen it runs
@smokeCritical paths, fastEvery commit and deployment
@regressionFull functional coverageNightly or before release
@slowLong testsNightly only
@api / @uiTest typeSeparate CI jobs
@checkout, @searchFeature areaWhen that feature changes
@flakyKnown unstable test under investigationExcluded from blocking runs

๐Ÿ’ก Tip: Agree on a short, fixed list of tags with your team and document it. Fifty random tags are as confusing as none.


๐Ÿ” Filter Tests with –grep

--grep (short: -g) runs only tests whose full title, including describe titles and tags, matches a regular expression:

npx playwright test --grep @smoke                 # tagged @smoke
npx playwright test -g "add two products"         # by title text
npx playwright test --grep "Cart"                 # every test inside the Cart describe
npx playwright test --grep-invert @slow           # everything except @slow
npx playwright test --grep "@smoke|@api"          # @smoke OR @api
npx playwright test --grep "(?=.*@smoke)(?=.*@checkout)"   # @smoke AND @checkout
npx playwright test --grep @regression --grep-invert @flaky

๐Ÿ“ Note: On Windows PowerShell, keep the quotes around patterns that contain |, otherwise the shell treats it as a pipe.

Check what would run without running it:

npx playwright test --grep @smoke --list

โš™๏ธ Filters in the Config and npm Scripts

Make common filters permanent:

// playwright.config.ts
export default defineConfig({
  grepInvert: /@flaky/,      // never run @flaky tests by default
  projects: [
    { name: 'smoke',      grep: /@smoke/,  use: { ...devices['Desktop Chrome'] } },
    { name: 'regression', grep: /@regression/, use: { ...devices['Desktop Chrome'] } },
  ],
});
{
  "scripts": {
    "test:smoke": "playwright test --grep @smoke",
    "test:regression": "playwright test --grep @regression --grep-invert @flaky",
    "test:api": "playwright test --grep @api"
  }
}

Then npm run test:smoke or npx playwright test --project=smoke. More command options are in Playwright CLI commands.


๐Ÿงต Control How a Group Runs: describe.configure

// Run tests in this file in parallel, even if fullyParallel is false
test.describe.configure({ mode: 'parallel' });

// Run tests in order; if one fails, the rest are skipped
test.describe.serial('Checkout wizard', () => {
  test('step 1: shipping', async ({ page }) => { /* ... */ });
  test('step 2: payment', async ({ page }) => { /* ... */ });
});

// Retries and timeout for one group
test.describe('Reports', () => {
  test.describe.configure({ retries: 2, timeout: 60_000 });
  test('export PDF', async ({ page }) => { /* ... */ });
});

โš ๏ธ Common mistake: Serial mode makes tests depend on each other, which slows the suite and spreads one failure to many tests. Prefer independent tests; use serial only for genuinely step-by-step flows.


๐ŸŽฏ Focus and Skip Groups While Developing

test.describe.only('Cart', () => { /* only this group runs */ });
test.describe.skip('Wishlist', () => { /* this group is skipped */ });
test.only('debug this one test', async ({ page }) => { /* ... */ });

Never commit .only: set forbidOnly: !!process.env.CI in the config so CI fails if one slips through. Skipping with conditions, fixme and slow are covered in annotations.


๐Ÿ”Ž Read Tags at Runtime

test('tagged test', { tag: '@smoke' }, async ({ page }, testInfo) => {
  console.log(testInfo.tags); // ['@smoke']
});

This is useful in reporters, hooks and integrations with test management tools.


๐Ÿ“ Assignment

  1. Organize your Sauce Demo tests into describe blocks: Login, Cart (with nested Add items and Remove items) and Sorting.
  2. Tag two critical tests @smoke, all tests @regression, and one test @slow.
  3. Run only @smoke tests, then everything except @slow, and use --list to confirm each selection first.
  4. Run tests that are both @regression and @smoke using a lookahead pattern.
  5. Add test:smoke and test:regression scripts to package.json.
  6. Create a smoke project in the config with grep: /@smoke/ and run it with --project=smoke.
  7. Bonus: print testInfo.tags in an afterEach hook for every test.

โ“ FAQ

๐Ÿค” How do I tag tests in Playwright?

Pass a tag option: test('title', { tag: '@smoke' }, async ({ page }) => {}), or add @smoke to the test title. Tags can also be applied to a whole test.describe block.

๐Ÿค” How do I run only smoke tests in Playwright?

Run npx playwright test --grep @smoke, or create a project with grep: /@smoke/ and run it with --project.

๐Ÿค” How do I exclude tests in Playwright?

Use --grep-invert @tag on the command line, or grepInvert in the config.

๐Ÿค” What does test.describe do in Playwright?

It groups tests under a shared title, scopes hooks and configuration to that group, and makes reports easier to read.

๐Ÿค” How do I run Playwright tests in a specific order?

Use test.describe.serial or test.describe.configure({ mode: 'serial' }). Tests then run one after another in declaration order, and the rest are skipped if one fails.


๐ŸŽฏ Next Steps

๐ŸŽฌ Watch the full Playwright tutorial series on YouTube!

Subscribe to SqaToolsByDY • Practice on sqatools.in

2 thoughts on “Group, Tag and Filter Playwright Tests (describe, Tags and grep)”

Leave a Comment