📖 The Story: The Mega-Repo Monster

A rapidly growing enterprise had 10 product teams. They all contributed to a single, massive Playwright repository. There was one playwright.config.ts file, one tests/ folder, and one CI pipeline.

When the Checkout team changed a shared Page Object, it broke the Profile team's tests. When the Auth team pushed a new test, CI took 2 hours to run the entire 5,000-test suite. Developers were blocked. The "Mega-Repo Monster" was slowing down the entire company.

The QA Architect, Elena, stepped in. She refactored the repository into an Nx Monorepo. She created a @pw/core package containing base fixtures, custom matchers, and shared utilities. Each team got their own app folder with their own playwright.config.ts that extended the base.

Most importantly, she implemented Test Impact Analysis in CI. When the Checkout team pushed code, CI only ran Checkout tests. The 2-hour CI block dropped to 8 minutes. The teams were unblocked, and the Monster was tamed.

Scaling tests is harder than writing them. Without architecture, success becomes your downfall.

🎯 Why Enterprise Architecture?

📁

Isolation

Teams can push tests without breaking unrelated suites. Code ownership is clear.

♻️

Reusability

Share base fixtures, custom matchers, and Page Objects via an internal npm package.

Fast CI

Run only the tests affected by the code change, not the entire 10,000-test suite.

🛠️

Config Inheritance

Enforce global standards (like timeouts) while allowing team-specific overrides.

🌍 Real World Example

The Auth team needs to test login. The Checkout team needs to test purchasing. Both need the LoginPage Page Object and the authenticatedBrowser fixture. Instead of copying and pasting this code, both teams import @pw/core. When the login UI changes, the Auth team updates @pw/core, publishes a new version, and both teams' tests are instantly fixed.

🧒 Explain Like I'm 10

Imagine a huge apartment building with 100 families. If there is only one main fuse box, when one family blows a fuse, the whole building goes dark.

A monorepo with sub-suites is like giving each family their own fuse box. If the Checkout family blows a fuse, only their lights go out. But they all still share the same building foundation (the @pw/core package).

🎓 Professional Explanation

Enterprise scaling relies on Monorepo tools (like Nx or Turborepo) combined with Playwright's fixture extension system. A core package (packages/playwright-core) exports a base test object configured with global fixtures (auth, DB, API clients). Individual team suites (apps/checkout-tests) import this base test object and use test.extend to add team-specific logic. CI pipelines use Nx's "Affected" commands or Playwright's --project tags to run only the tests related to the changed code.

📊 Monolith vs Monorepo

Repository Structures

Monolith (Bad)
1 playwright.config.ts
/tests (All teams mixed)
1 CI Pipeline (Runs everything)
High risk of cross-team breaks
Monorepo (Good)
packages/playwright-core
apps/auth-tests (config.ts)
apps/checkout-tests (config.ts)
CI runs only affected apps

📦 Step 1: The Shared Core Package

Create a package that exports your base fixtures. This is the foundation of your enterprise architecture.

typescript · packages/playwright-core/src/index.ts
import { test as base } from '@playwright/test';
import { AuthFixture } from './auth';
import { DbFixture } from './db';

// Extend the base test with enterprise-wide fixtures
export const test = base.extend<AuthFixture & DbFixture>({
  // Auth fixture available to all teams
  auth: async ({ page }, use) => {
    // ... inject storageState logic
    await use('authenticated');
  },
  // DB fixture available to all teams
  db: async ({}, use) => {
    // ... pool logic
    await use(pool);
  }
});

export { expect } from '@playwright/test';

⚙️ Step 2: Config Inheritance

Each team imports the core test object and can extend it further or configure their own Playwright config.

typescript · apps/checkout-tests/playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  // Only run tests in this app's folder
  testDir: './tests',
  use: {
    baseURL: 'https://staging.checkout.company.com',
    /* Team-specific overrides */
  },
  projects: [
    { name: 'checkout-chrome', use: { browserName: 'chromium' } }
  ]
});

And in the test files, they import test from @pw/core instead of @playwright/test.

typescript · apps/checkout-tests/tests/checkout.spec.ts
// Import the enterprise base test!
import { test, expect } from '@pw/core';

test('complete checkout', async ({ page, auth, db }) => {
  // auth and db are automatically available!
});

🚀 Step 3: Intelligent CI Execution

Running 10,000 tests on every PR is impossible. Use Nx Affected to only run tests for apps that changed.

bash · CI Pipeline
# Only run Playwright tests for apps affected by this PR
npx nx affected -t e2e

If the PR only touched apps/checkout, Nx skips apps/auth-tests, saving 30 minutes of CI time.

⚠️ Common Mistakes

Mistake 1: Circular Dependencies

The @pw/core package must be completely independent. It cannot import code from apps/checkout. Keep the flow one-directional: Apps depend on Core, never the other way around.

Mistake 2: Global Teardown Collisions

If @pw/core has a global teardown that cleans the database, and 5 apps run in parallel, they might trigger 5 teardowns at once. Ensure teardowns are idempotent or scoped to the specific app running.

✅ Best Practices

  1. Use Nx or Turborepo. These tools provide the caching and "affected" logic necessary for large monorepos.
  2. Version your Core Package. If @pw/core introduces a breaking change, use semver so teams can upgrade at their own pace.
  3. Enforce ESLint rules. Prevent teams from importing @playwright/test directly; force them to use @pw/core.
  4. Centralize Reporting. Have all apps output their HTML reports to a shared CI artifact directory so developers can view them in one place.

🏗️ Senior Engineer Deep Dive

Test Impact Analysis (TIA)

TIA is the holy grail of enterprise testing. Nx analyzes your TypeScript dependency graph. If you change a file in libs/checkout-ui, Nx knows exactly which test files (across all apps) import that library. It runs only those specific tests. This turns a 30-minute suite into a 30-second suite for small PRs.

Micro-Frontend (MFE) Architecture

In an MFE setup, different teams own different parts of the UI (e.g., Auth MFE, Cart MFE). Playwright tests should map to this architecture. The Auth team tests the Auth MFE in isolation (perhaps mocking the Cart MFE). A separate "Integration" app tests them all stitched together. This prevents the "Mega-Repo" monster.

💼 Interview Questions

Beginner
Why is a single Playwright config problematic for a 10-team company?
Show Answer
A single config forces all teams to run under the same rules, timeouts, and CI pipeline. If one team breaks a shared Page Object, all 10 teams' tests fail, blocking the entire company's CI.
Intermediate
How do you share custom fixtures across multiple teams?
Show Answer
I create a @pw/core package in the monorepo. This package imports the base test from Playwright, uses test.extend to add global fixtures (like auth or DB), and exports it. Teams import this extended test instead of the standard one.
Advanced
How do you optimize CI execution for 5,000 Playwright tests?
Show Answer
I use a monorepo tool like Nx to leverage Test Impact Analysis (TIA). TIA analyzes the code dependency graph and runs only the tests associated with the changed code. Additionally, I use Playwright's sharding to split large suites across multiple CI machines.
Scenario
Team A updates a shared button component in @pw/core, breaking Team B's tests. How do you prevent this?
Show Answer
Before merging the change, CI should run the test suites for all apps that depend on @pw/core (using Nx Affected). If Team B's tests break, the PR is blocked. Team A must coordinate with Team B or update the tests in the same PR.

🏋️ Practical Exercise

🎯 Hands-On Practice Hard

Extract a Core Package

  1. Take an existing Playwright project.
  2. Create a packages/core folder.
  3. Move your authentication fixture and storageState logic into packages/core/index.ts.
  4. Export the extended test object.
  5. In your main test folder, change the imports from @playwright/test to your local @pw/core package.

🚀 Mini Project

🏗️ The Nx Monorepo Setup

  1. Create a new Nx workspace: npx create-nx-workspace@latest.
  2. Add two Playwright apps: auth-tests and cart-tests.
  3. Create a shared library: libs/playwright-core.
  4. Export a custom fixture from the lib that logs "Test Started" to the console.
  5. Use that fixture in both apps. Verify they run independently.

📝 Quiz

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

1. What is the primary benefit of a Monorepo for Playwright?
A) It runs tests faster locally.
B) It allows code sharing and isolated CI execution across teams.
C) It replaces the need for GitHub.
D) It automatically fixes flaky tests.
Answer: B — Teams can share utilities while running only their specific tests in CI.
2. Where should global fixtures (like Auth) live in an enterprise setup?
A) In every individual test file
B) In the playwright.config.ts
C) In a shared @pw/core package
D) In the CI pipeline script
Answer: C — A shared core package ensures all teams use the same authentication logic.
3. How does Test Impact Analysis (TIA) speed up CI?
A) It skips all tests and just deploys.
B) It analyzes the dependency graph to run only tests related to the changed code.
C) It runs tests on faster hardware.
D) It deletes old tests.
Answer: B — TIA ensures you don't run 5,000 tests when only 10 are affected by the PR.
4. What does the @pw/core package export?
A) Only the expect function
B) Only the Playwright config
C) A base test object extended with shared fixtures
D) HTML reports
Answer: C — It exports a customized test object that teams import instead of the default Playwright one.
5. Can different teams have different playwright.config.ts files?
A) Yes, each app in a monorepo can have its own config.
B) No, there can only be one per repository.
C) Yes, but they must be identical.
D) No, Playwright ignores multiple configs.
Answer: A — Each team can override settings like baseURL or timeouts specific to their app.
6. Why is a "Mega-Repo" (single config, single test folder) an anti-pattern?
A) It is too secure.
B) A change by Team A can break Team B, and CI runs everything, taking hours.
C) It doesn't support TypeScript.
D) Playwright limits repos to 100 tests.
Answer: B — Cross-team dependencies cause cascading failures and massive CI bottlenecks.
7. Which tools are commonly used to manage Playwright Monorepos?
A) Webpack and Babel
B) Nx and Turborepo
C) Jest and Mocha
D) Docker and Kubernetes
Answer: B — Nx and Turborepo provide the caching and TIA necessary for large JS/TS monorepos.
8. What is a circular dependency in this context?
A) When a test runs in a loop
B) When @pw/core imports code from an app, or apps import from each other
C) When the database reconnects
D) When two tests share data
Answer: B — Dependencies must flow one way (Apps -> Core). Core cannot depend on Apps.
9. How can you enforce that teams use the @pw/core test object?
A) By disabling the standard test import via ESLint
B) By deleting node_modules/@playwright/test
C) By writing it in the README
D) By setting a CI secret
Answer: A — An ESLint rule can flag direct imports from @playwright/test in test files.
10. What is the benefit of mapping tests to Micro-Frontends (MFEs)?
A) It makes tests run in the cloud.
B) It allows teams to test their specific MFE in isolation, mocking the others.
C) It replaces the need for E2E tests.
D) It allows tests to be written in Python.
Answer: B — It maintains team autonomy and prevents cross-MFE breakages.

❓ FAQ

Q: Can we do this with multiple Git repositories instead of a Monorepo?

A: Yes, you can publish @pw/core to a private npm registry. However, cross-repo CI triggers are much harder to set up than Nx Affected in a monorepo. Monorepos are generally preferred for test automation.

Q: Does Nx support Playwright out of the box?

A: Yes, Nx provides an @nx/playwright plugin that generates the app structure and configures the Nx cache for test results.

📦 Summary

🎯 Key Takeaways

  • Use a Monorepo (Nx or Turborepo) to scale Playwright across teams.
  • Create a @pw/core package to share base fixtures and matchers.
  • Allow config inheritance so teams can override settings while adhering to global standards.
  • Use Test Impact Analysis (TIA) to run only tests affected by a PR.
  • Avoid circular dependencies. Apps depend on Core, never vice versa.
  • Enforce the core package using ESLint rules.