</>

Technology

Playwright

Difficulty

Advanced

Interview Question

How would you structure a Playwright test framework for a team of 10 developers?

Answer

Scenario: Enterprise Playwright Framework Design for a Team

Project Structure

CODE
playwright-framework/
├── pages/                    # Page Object Model
│   ├── BasePage.ts
│   ├── auth/
│   │   ├── LoginPage.ts
│   │   └── RegisterPage.ts
│   ├── products/
│   │   ├── ProductListPage.ts
│   │   └── ProductDetailPage.ts
│   └── checkout/
│       ├── CartPage.ts
│       ├── ShippingPage.ts
│       └── PaymentPage.ts
│
├── fixtures/                 # Custom test fixtures
│   ├── index.ts              # Re-exports all fixtures
│   ├── page-fixtures.ts      # Page objects as fixtures
│   └── auth-fixtures.ts      # Auth state fixtures
│
├── helpers/                  # Shared utilities
│   ├── api.helper.ts         # API setup/teardown
│   ├── database.helper.ts    # Direct DB operations
│   ├── date.helper.ts
│   └── random.helper.ts      # Test data generators
│
├── test-data/               # Test data management
│   ├── users.ts             # User constants/factories
│   ├── products.ts          # Product test data
│   └── factories/           # Data factories
│       └── user.factory.ts
│
├── tests/                   # Test files by feature
│   ├── auth/
│   │   ├── login.spec.ts
│   │   └── register.spec.ts
│   ├── products/
│   │   └── product-listing.spec.ts
│   ├── checkout/
│   │   └── checkout.spec.ts
│   └── smoke/
│       └── smoke.spec.ts
│
├── .auth/                   # Auth state files (gitignored)
│   ├── user.json
│   └── admin.json
│
├── global-setup.ts
├── global-teardown.ts
├── playwright.config.ts
└── .env.example

playwright.config.ts (Team-Standard Config)

TypeScript
import { defineConfig, devices } from '@playwright/test';
import dotenv from 'dotenv';
dotenv.config({ path: `.env.${process.env.TEST_ENV || 'staging'}` });

export default defineConfig({
  testDir:      './tests',
  fullyParallel: true,
  workers:      process.env.CI ? 4 : 2,
  retries:      process.env.CI ? 2 : 0,
  globalSetup:  './global-setup.ts',
  globalTeardown: './global-teardown.ts',

  reporter: [
    ['html', { outputFolder: 'playwright-report' }],
    ['json', { outputFile: 'results/results.json' }],
    process.env.CI ? ['github'] : ['list'],
  ],

  use: {
    baseURL:    process.env.BASE_URL || 'http://localhost:3000',
    headless:   true,
    trace:      'retain-on-failure',
    screenshot: 'only-on-failure',
    video:      'retain-on-failure',
    actionTimeout:     15_000,
    navigationTimeout: 30_000,
  },

  projects: [
    { name: 'setup', testMatch: '**/*.setup.ts' },
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'], storageState: '.auth/user.json' },
      dependencies: ['setup'],
    },
  ],
});

Data Factory Pattern

TypeScript
// helpers/factories/user.factory.ts
export function createUser(overrides: Partial<User> = {}): User {
  return {
    email:    `user-${Date.now()}-${Math.random().toString(36).slice(2)}@test.com`,
    password: 'TestPass123!',
    name:     'Test User',
    role:     'viewer',
    ...overrides,
  };
}

// Usage in test
const adminUser = createUser({ role: 'admin', email: 'admin@test.com' });

Team Guidelines

  1. One POM class per page — pages should have no test logic
  2. Use fixtures for all shared setup — no copy-paste across tests
  3. API setup for test data — never create data via UI in beforeEach
  4. Tag all tests@smoke, @regression, @api, @auth
  5. No hard-coded waits — use waitFor() or assertions
  6. Descriptive test names'user with expired session is redirected to login'
  7. Teardown via API — clean up test data after tests that create records
  8. Store secrets in CI — never in code or .env files committed to git

Follow AutomateQA

Related Topics