Enterprise Architecture — Scaling to 10,000 Tests
When 10 teams push to the same test repo, chaos ensues. Learn how to scale Playwright across an enterprise using Monorepos, shared base fixtures, config inheritance, and intelligent CI execution.
📖 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)
Monorepo (Good)
📦 Step 1: The Shared Core Package
Create a package that exports your base fixtures. This is the foundation of your enterprise architecture.
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.
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.
// 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.
# 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
- Use Nx or Turborepo. These tools provide the caching and "affected" logic necessary for large monorepos.
- Version your Core Package. If
@pw/coreintroduces a breaking change, use semver so teams can upgrade at their own pace. - Enforce ESLint rules. Prevent teams from importing
@playwright/testdirectly; force them to use@pw/core. - 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
Show Answer
Show Answer
@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.Show Answer
@pw/core, breaking Team B's tests. How do you prevent this?Show Answer
@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
Extract a Core Package
- Take an existing Playwright project.
- Create a
packages/corefolder. - Move your authentication fixture and
storageStatelogic intopackages/core/index.ts. - Export the extended
testobject. - In your main test folder, change the imports from
@playwright/testto your local@pw/corepackage.
🚀 Mini Project
🏗️ The Nx Monorepo Setup
- Create a new Nx workspace:
npx create-nx-workspace@latest. - Add two Playwright apps:
auth-testsandcart-tests. - Create a shared library:
libs/playwright-core. - Export a custom fixture from the lib that logs "Test Started" to the console.
- Use that fixture in both apps. Verify they run independently.
📝 Quiz
Test your understanding. Click an option to check your answer.
playwright.config.ts@pw/core package@pw/core package export?expect functiontest object extended with shared fixturestest object that teams import instead of the default Playwright one.playwright.config.ts files?baseURL or timeouts specific to their app.@pw/core imports code from an app, or apps import from each other@pw/core test object?test import via ESLintnode_modules/@playwright/test@playwright/test in test files.❓ 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/corepackage 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.
