📖 The Story: The Config Monster

A growing QA team had a problem. They needed to run a fast "Smoke" suite on every Pull Request, but a massive "Regression" suite every night. They also needed to test Chrome, Firefox, and Safari.

To solve this, they created three config files: playwright.smoke.config.ts, playwright.regression.config.ts, and playwright.cross-browser.config.ts.

Within a year, the repo was a mess. A developer changing the base URL had to update three files. CI scripts were a tangled web of --config flags. They had created a Config Monster.

Then they discovered Playwright Projects. They consolidated everything into a single playwright.config.ts. They defined projects for Smoke, Regression, Chromium, and WebKit. The CI pipeline simply called --project=Smoke. The Config Monster was slain.

One config file to rule them all. Projects are the enterprise standard for test execution.

🎯 Why Use Projects?

📂

Single Source of Truth

Maintain one config file instead of a dozen fragmented variations.

Flexible Execution

Run just the smoke tests, just the API tests, or just the mobile tests via a single CLI flag.

🔗

Dependencies

Guarantee that a "Setup" project (like logging in) runs before the main test projects.

📊

Clean Reporting

The HTML report groups results by project, making it easy to see "Chrome passed but Firefox failed."

🌍 Real World Example

You want to test a desktop app on Chrome and a mobile app on Safari iOS. In your config, you define a Desktop Chrome project and an iPhone 14 project. When you run npx playwright test, it runs both. If you only want to test mobile today, you run npx playwright test --project="iPhone 14".

🧒 Explain Like I'm 10

Imagine a restaurant menu. Instead of having a "Breakfast Menu", "Lunch Menu", and "Dinner Menu" printed on separate pieces of paper, you have one big menu. The items are grouped into sections: "Breakfast", "Lunch", and "Dinner".

If you want breakfast, you just order from the Breakfast section. Playwright Projects are those sections. One menu (config file), multiple sections (projects).

🎓 Professional Explanation

The projects array in playwright.config.ts allows you to define multiple execution environments or test groupings. Each project can override top-level use properties (like browserName, viewport, or baseURL) and filter tests using testMatch or grep.

When Playwright runs without a --project flag, it executes all projects sequentially (or in parallel if configured). When a --project flag is passed, it runs only that specific project.

📊 Single Config vs Projects

Organizing Your Test Suite

The Bad Way (Multiple Files)
playwright.smoke.config.ts
playwright.regression.config.ts
playwright.mobile.config.ts
⬇️
CI: npx playwright test --config=...
The Playwright Way (Projects)
playwright.config.ts
⬇️ Contains Array of Projects
Project: Smoke (grep: @smoke)
Project: Regression (grep: @regression)
Project: Mobile (device: iPhone)
⬇️
CI: npx playwright test --project=Smoke

💨 Smoke vs. Regression Projects

The most common use case is splitting tests by speed/importance. You can tag tests in your code, and filter them in the project.

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

export default defineConfig({
  testDir: './tests',
  projects: [
    {
      name: 'Smoke',
      testMatch: ['**/*.smoke.ts'], // OR use grep: /@smoke/
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'Regression',
      testIgnore: ['**/*.smoke.ts'], // Run everything except smoke
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'Mobile Safari',
      testMatch: ['**/*.mobile.ts'],
      use: { ...devices['iPhone 14'] },
    }
  ]
});

Grep vs TestMatch: You can use testMatch to match file names (e.g., login.smoke.ts), or you can use grep: /@smoke/ to match test titles like test('login @smoke', ...). Both are valid; grep is often easier to maintain.

🔗 Project Dependencies (Setup Projects)

Sometimes Project B relies on Project A finishing first. For example, a "Setup" project logs in and saves the storageState. The "Chromium" project depends on that state.

typescript · playwright.config.ts
projects: [
  {
    name: 'Setup',
    testMatch: '/.*\.setup\.ts/',
  },
  {
    name: 'Chromium',
    dependencies: ['Setup'], // Waits for Setup to finish first
    use: {
      storageState: 'auth-state.json', // Uses the file Setup created
      ...devices['Desktop Chrome']
    },
  }
]

⚙️ Running Projects via CLI

bash
# Run everything (Smoke, Regression, Mobile)
npx playwright test

# Run ONLY the Smoke project
npx playwright test --project=Smoke

# Run both Smoke and Mobile Safari
npx playwright test --project=Smoke --project="Mobile Safari"

⚠️ Common Mistakes

Mistake 1: Overlapping Test Files

If Project A uses testMatch: ['**/*.ts'] and Project B uses testMatch: ['**/*.spec.ts'], Project A will run all tests, including B's tests. Use testIgnore to keep them strictly separate.

Mistake 2: Forgetting Device Context

typescript · ❌ Missing Device
// ❌ BAD: This doesn't test mobile, it just names it mobile
{
  name: 'Mobile',
  use: { browserName: 'chromium' }
}

// ✅ GOOD: Use Playwright's device descriptors
{
  name: 'Mobile',
  use: { ...devices['Pixel 7'] }
}

✅ Best Practices

  1. Name projects clearly. "Desktop Chrome" is better than "chromium". "Mobile Safari" is better than "webkit".
  2. Use dependencies for auth. It's cleaner than using globalSetup for multi-role authentication.
  3. Keep Smoke tests under 3 minutes. A PR shouldn't wait 20 minutes for a smoke suite.
  4. Use tags (grep). Tagging tests with @critical or @flaky allows you to group them in projects without moving files around.

🏗️ Senior Engineer Deep Dive

Execution Order and Parallelism

By default, projects run sequentially. The "Smoke" project finishes entirely before "Regression" starts. However, within each project, tests run in parallel across workers. If you have dependencies, Playwright guarantees the dependency project completes all its tests (and workers) before spawning workers for the dependent project.

Environment Variables per Project

You can inject specific env vars per project using use: { env: { ADMIN_TOKEN: 'xyz' } }. This is incredibly powerful for testing multi-tenant architectures where Test A acts as Admin and Test B acts as a standard User, without them colliding on the same database row.

💼 Interview Questions

Beginner
How do you run tests only for a specific browser or device?
Show Answer
I define a "project" in playwright.config.ts with a specific name and device configuration (e.g., iPhone 14). Then I run npx playwright test --project="iPhone 14".
Intermediate
How do you separate Smoke tests from Regression tests in Playwright?
Show Answer
I create two projects in the config file. I can either use testMatch to match different file extensions (e.g., *.smoke.ts) or use the grep option to match test titles tagged with @smoke. Then I run the specific project via the CLI.
Advanced
Explain how Project Dependencies work and when to use them.
Show Answer
Project dependencies ensure one project runs completely before another starts. I use this for authentication setup. I create a "Setup" project that logs in and saves the storageState. The main test project lists "Setup" in its dependencies array, ensuring the auth state file exists before the tests try to use it.
Scenario
Your CI pipeline needs to run a fast suite on PRs and a full suite on merges to main. How do you configure this?
Show Answer
I define both "Smoke" and "Regression" projects in a single playwright.config.ts. The PR CI job runs npx playwright test --project=Smoke. The Main branch CI job runs npx playwright test (which executes all projects, including Regression).

🏋️ Practical Exercise

🎯 Hands-On Practice Medium

Split Your Suite

  1. Open your playwright.config.ts.
  2. Create two projects: "Smoke" and "Regression".
  3. Tag a few of your tests with @smoke in their titles.
  4. Configure the Smoke project to grep: /@smoke/.
  5. Run npx playwright test --project=Smoke and verify only the tagged tests run.

🚀 Mini Project

🏗️ The Multi-Device Matrix

  1. Configure 3 projects in your config: "Desktop Chrome", "Desktop Firefox", and "Mobile Safari".
  2. Write a single test that navigates to a responsive website.
  3. Run the full suite (all 3 projects).
  4. Open the HTML report. Observe how Playwright groups the test results by project, showing how the test performed across all 3 environments.

📝 Quiz

Test your understanding. Click an option to check your answer.

1. Where are Playwright Projects defined?
A) In the package.json file
B) In the projects array inside playwright.config.ts
C) As separate files in the tests/ directory
D) In the CI pipeline YAML
Answer: B — Projects are configured as an array of objects in the main Playwright config file.
2. How do you run only the "Smoke" project via the CLI?
A) npx playwright test --grep=Smoke
B) npx playwright test --project=Smoke
C) npx playwright smoke
D) npx playwright test --config=Smoke
Answer: B — The --project flag filters execution to the specified project name.
3. What does the dependencies property do in a project?
A> Downloads required npm packages
B) Ensures one project finishes running before the current project starts
C) Links tests to the Page Object Model
D) Shares variables between tests
Answer: B — Dependencies guarantee execution order, perfect for Setup -> Test flows.
4. How can you run tests only on an iPhone 14?
A) Set viewport: { width: 390, height: 844 }
B) Use use: { ...devices['iPhone 14'] } inside a project
C) Use page.setMobile()
D) Playwright cannot test iOS
Answer: B — Spreading devices['iPhone 14'] into use sets the correct viewport, userAgent, and touch events.
5. What is the main benefit of using Projects instead of multiple config files?
A) It runs faster.
B) It allows cross-browser testing.
C) It provides a single source of truth for all test environments.
D) It bypasses CORS restrictions.
Answer: C — Projects keep all configuration in one place, avoiding the "Config Monster" fragmentation.
6. If you run npx playwright test without any flags, what happens?
A) It runs the first project only.
B) It runs all defined projects.
C) It asks you to choose a project.
D) It runs only Chrome by default.
Answer: B — Without a filter, Playwright executes every project in the array sequentially.
7. How can you tag a test to be included in a "Smoke" project?
A) test('login', { tag: 'smoke' })
B) Name the file login.smoke.ts and use testMatch
C) Include @smoke in the test title and use grep
D) Both B and C are valid approaches
Answer: D — You can filter by file name pattern or by searching the test title with grep.
8. How does the HTML report display results from multiple projects?
A) It mixes them all together randomly.
B) It creates a separate HTML file for each project.
C) It groups the results by project name for easy filtering.
D) It only shows the first project that failed.
Answer: C — The report UI provides tabs/filters to view results per project.
9. Can a project override global use settings?
A) Yes, settings inside the project's use block override the global ones.
B) No, global settings are immutable.
C) Only if you use TypeScript.
D) Yes, but only for timeouts.
Answer: A — Project-level use properties take precedence over top-level use properties.
10. What is a common use case for project dependencies?
A) Downloading browser binaries
B) Running an authentication setup project before the main E2E tests
C) Generating HTML reports
D) Sharding tests across multiple machines
Answer: B — A setup project can log in and save storageState, which the dependent project then uses.

❓ FAQ

Q: Can projects run in parallel with each other?

A: By default, projects run sequentially. However, there is a projects configuration in experimental phases (or you can use CI matrices) to run them simultaneously. Usually, sequential is fine because tests within the project are already running in parallel.

Q: Should I put Smoke and Regression tests in different folders?

A: It's up to you. Some prefer tests/smoke/ and tests/regression/. Others keep them in the same folder but use grep tags. Tags are often easier to maintain.

📦 Summary

🎯 Key Takeaways

  • Projects allow multiple execution environments (browsers, devices, suites) in one config file.
  • Use --project=Name to run specific suites via CLI.
  • Use dependencies to guarantee setup projects run first.
  • Use devices for accurate mobile emulation.
  • Keep CI scripts clean by filtering at the project level, not the file level.