QA Engineer Skills 2026QA-2026Production Framework

Production Framework

A production test framework is more than a collection of test files. It needs configuration management for multiple environments, custom reporters for team visibility, sensible project structure that scales, and conventions that new team members can follow without a manual.


Project Structure at Scale

e2e/
├── playwright.config.ts         # Main configuration
├── global-setup.ts              # One-time setup (DB seed, auth)
├── global-teardown.ts           # One-time cleanup
├── fixtures/
│   ├── index.ts                 # Re-exports all custom fixtures
│   ├── auth.fixture.ts          # Authentication fixtures
│   └── data.fixture.ts          # Test data fixtures
├── pages/
│   ├── base.page.ts             # Base page object with shared methods
│   ├── login.page.ts
│   ├── dashboard.page.ts
│   └── components/
│       ├── header.component.ts
│       └── modal.component.ts
├── tests/
│   ├── auth/
│   │   ├── login.spec.ts
│   │   └── registration.spec.ts
│   ├── checkout/
│   │   ├── cart.spec.ts
│   │   └── payment.spec.ts
│   └── admin/
│       └── user-management.spec.ts
├── test-data/
│   ├── users.json
│   └── products.json
└── utils/
    ├── api-client.ts            # API helper for setup/teardown
    └── test-helpers.ts          # Shared utility functions

Key Conventions

  • One spec file per feature area, not per page — tests are organized by what they verify
  • Page objects in pages/, fixtures in fixtures/ — clear separation
  • Shared test data in test-data/ — not hardcoded in tests
  • Utilities in utils/ — API clients, date helpers, generators

Configuration Management

Multi-Environment Setup

// playwright.config.ts
import { defineConfig } from '@playwright/test';

const env = process.env.TEST_ENV || 'staging';

const envConfig = {
  local: { baseURL: 'http://localhost:3000' },
  staging: { baseURL: 'https://staging.example.com' },
  production: { baseURL: 'https://example.com' },
};

export default defineConfig({
  testDir: './tests',
  timeout: 60_000,
  retries: env === 'production' ? 0 : 2,
  workers: env === 'local' ? undefined : 4,
  use: {
    baseURL: envConfig[env].baseURL,
    screenshot: 'only-on-failure',
    trace: 'on-first-retry',
    video: 'on-first-retry',
  },
  projects: [
    { name: 'setup', testMatch: /.*\.setup\.ts/ },
    {
      name: 'chromium',
      dependencies: ['setup'],
      use: { browserName: 'chromium', storageState: '.auth/user.json' },
    },
    {
      name: 'firefox',
      dependencies: ['setup'],
      use: { browserName: 'firefox', storageState: '.auth/user.json' },
    },
  ],
});

Running Against Different Environments

# Default (staging)
npx playwright test

# Local development
TEST_ENV=local npx playwright test

# Production smoke tests
TEST_ENV=production npx playwright test --grep @smoke

Test Tagging

Use tags to categorize tests for selective execution:

test('@smoke login with valid credentials', async ({ page }) => {
  // Critical path — runs on every deployment
});

test('@regression cart handles 100 items', async ({ page }) => {
  // Thorough — runs nightly
});

test('@slow report generation with large dataset', async ({ page }) => {
  // Resource-intensive — runs on-demand
});
npx playwright test --grep @smoke        # Only smoke tests
npx playwright test --grep-invert @slow  # Everything except slow tests

Custom Reporters

Playwright supports multiple reporters simultaneously:

// playwright.config.ts
export default defineConfig({
  reporter: [
    ['list'],                           // Console output
    ['html', { open: 'never' }],        // HTML report
    ['json', { outputFile: 'results.json' }],  // Machine-readable
    ['junit', { outputFile: 'results.xml' }],  // CI integration
  ],
});

Global Setup and Teardown

// global-setup.ts
import { chromium, FullConfig } from '@playwright/test';

async function globalSetup(config: FullConfig) {
  // Seed the database
  // Create test users via API
  // Warm up the application
}

export default globalSetup;
// playwright.config.ts
export default defineConfig({
  globalSetup: require.resolve('./global-setup'),
  globalTeardown: require.resolve('./global-teardown'),
});

Key Takeaways

  • Organize by feature (not by page) — tests, page objects, and fixtures each have their own directory
  • Use environment variables for multi-environment configuration — one config file, multiple targets
  • Tag tests (@smoke, @regression, @slow) for selective execution
  • Stack reporters (HTML + JSON + JUnit) for different audiences
  • Global setup handles one-time tasks like database seeding and authentication