Back to Sanity

Projects & Dependencies

.agents/skills/playwright-best-practices/core/projects-dependencies.md

5.20.08.7 KB
Original Source

Projects & Dependencies

Table of Contents

  1. Project Configuration
  2. Project Dependencies
  3. Setup Projects
  4. Filtering & Running Projects
  5. Sharing Configuration
  6. Advanced Patterns

Project Configuration

Basic Multi-Browser Setup

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

export default defineConfig({
  projects: [
    {
      name: 'chromium',
      use: {...devices['Desktop Chrome']},
    },
    {
      name: 'firefox',
      use: {...devices['Desktop Firefox']},
    },
    {
      name: 'webkit',
      use: {...devices['Desktop Safari']},
    },
  ],
})

Environment-Based Projects

typescript
export default defineConfig({
  projects: [
    {
      name: 'staging',
      use: {
        baseURL: 'https://staging.example.com',
      },
    },
    {
      name: 'production',
      use: {
        baseURL: 'https://example.com',
      },
    },
    {
      name: 'local',
      use: {
        baseURL: 'http://localhost:3000',
      },
    },
  ],
})

Test Type Projects

typescript
export default defineConfig({
  projects: [
    {
      name: 'e2e',
      testDir: './tests/e2e',
      use: {...devices['Desktop Chrome']},
    },
    {
      name: 'api',
      testDir: './tests/api',
      use: {baseURL: 'http://localhost:3000'},
    },
    {
      name: 'visual',
      testDir: './tests/visual',
      use: {
        ...devices['Desktop Chrome'],
        viewport: {width: 1280, height: 720},
      },
    },
  ],
})

Project Dependencies

Setup Dependency

typescript
export default defineConfig({
  projects: [
    // Setup project runs first
    {
      name: 'setup',
      testMatch: /.*\.setup\.ts/,
    },

    // Browser projects depend on setup
    {
      name: 'chromium',
      use: {
        ...devices['Desktop Chrome'],
        storageState: '.auth/user.json',
      },
      dependencies: ['setup'],
    },
    {
      name: 'firefox',
      use: {
        ...devices['Desktop Firefox'],
        storageState: '.auth/user.json',
      },
      dependencies: ['setup'],
    },
  ],
})

Multiple Auth States

typescript
export default defineConfig({
  projects: [
    // Auth setup projects
    {
      name: 'setup-admin',
      testMatch: /admin\.setup\.ts/,
    },
    {
      name: 'setup-user',
      testMatch: /user\.setup\.ts/,
    },

    // Admin tests
    {
      name: 'admin-tests',
      testDir: './tests/admin',
      use: {storageState: '.auth/admin.json'},
      dependencies: ['setup-admin'],
    },

    // User tests
    {
      name: 'user-tests',
      testDir: './tests/user',
      use: {storageState: '.auth/user.json'},
      dependencies: ['setup-user'],
    },

    // Tests that need both
    {
      name: 'integration-tests',
      testDir: './tests/integration',
      dependencies: ['setup-admin', 'setup-user'],
    },
  ],
})

Chained Dependencies

typescript
export default defineConfig({
  projects: [
    // Step 1: Database setup
    {
      name: 'db-setup',
      testMatch: /db\.setup\.ts/,
    },

    // Step 2: Auth setup (needs DB)
    {
      name: 'auth-setup',
      testMatch: /auth\.setup\.ts/,
      dependencies: ['db-setup'],
    },

    // Step 3: Seed data (needs auth)
    {
      name: 'seed-setup',
      testMatch: /seed\.setup\.ts/,
      dependencies: ['auth-setup'],
    },

    // Tests (need everything)
    {
      name: 'tests',
      testDir: './tests',
      dependencies: ['seed-setup'],
    },
  ],
})

Setup Projects

Authentication Setup

Setup projects are the recommended way to handle authentication. They run before your main test projects and can use Playwright fixtures.

For complete authentication patterns (storage state, multiple auth states, auth fixtures), see fixtures-hooks.md.

Data Seeding Setup

typescript
// seed.setup.ts
import {test as setup} from '@playwright/test'

setup('seed test data', async ({request}) => {
  // Create test data via API
  await request.post('/api/test/seed', {
    data: {
      users: 10,
      products: 50,
      orders: 100,
    },
  })
})

Cleanup Setup

typescript
// cleanup.setup.ts
import {test as setup} from '@playwright/test'

setup('cleanup previous run', async ({request}) => {
  // Clean up data from previous test runs
  await request.delete('/api/test/cleanup')
})

Filtering & Running Projects

Run Specific Project

bash
# Run single project
npx playwright test --project=chromium

# Run multiple projects
npx playwright test --project=chromium --project=firefox

Run by Grep

bash
# Run tests matching pattern
npx playwright test --grep @smoke

# Run project with grep
npx playwright test --project=chromium --grep @critical

# Exclude pattern
npx playwright test --grep-invert @slow

Project-Specific Grep

typescript
export default defineConfig({
  projects: [
    {
      name: 'smoke',
      grep: /@smoke/,
      use: {...devices['Desktop Chrome']},
    },
    {
      name: 'regression',
      grepInvert: /@smoke/,
      use: {...devices['Desktop Chrome']},
    },
  ],
})

Sharing Configuration

Base Configuration

typescript
// playwright.config.ts
const baseConfig = {
  timeout: 30000,
  expect: {timeout: 5000},
  use: {
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
  },
}

export default defineConfig({
  ...baseConfig,
  projects: [
    {
      name: 'chromium',
      use: {
        ...baseConfig.use,
        ...devices['Desktop Chrome'],
      },
    },
    {
      name: 'firefox',
      use: {
        ...baseConfig.use,
        ...devices['Desktop Firefox'],
      },
    },
  ],
})

Shared Project Settings

typescript
const sharedBrowserConfig = {
  timeout: 60000,
  retries: 2,
  use: {
    video: 'on-first-retry',
    trace: 'on-first-retry',
  },
}

export default defineConfig({
  projects: [
    {
      name: 'chromium',
      ...sharedBrowserConfig,
      use: {
        ...sharedBrowserConfig.use,
        ...devices['Desktop Chrome'],
      },
    },
    {
      name: 'firefox',
      ...sharedBrowserConfig,
      use: {
        ...sharedBrowserConfig.use,
        ...devices['Desktop Firefox'],
      },
    },
  ],
})

Advanced Patterns

Conditional Projects

typescript
const projects = [
  {
    name: 'chromium',
    use: {...devices['Desktop Chrome']},
  },
]

// Add Firefox only in CI
if (process.env.CI) {
  projects.push({
    name: 'firefox',
    use: {...devices['Desktop Firefox']},
  })
}

// Add mobile only for specific test dirs
if (process.env.TEST_MOBILE) {
  projects.push({
    name: 'mobile',
    use: {...devices['iPhone 14']},
  })
}

export default defineConfig({projects})

Project Metadata

typescript
export default defineConfig({
  projects: [
    {
      name: 'chromium',
      use: {...devices['Desktop Chrome']},
      metadata: {
        platform: 'desktop',
        browser: 'chromium',
        priority: 'high',
      },
    },
  ],
})

// Access in test
test('example', async ({page}, testInfo) => {
  const {platform, priority} = testInfo.project.metadata
  console.log(`Running on ${platform} with ${priority} priority`)
})

Teardown Projects

typescript
export default defineConfig({
  projects: [
    {
      name: 'setup',
      testMatch: /.*\.setup\.ts/,
      teardown: 'teardown', // Run teardown after this completes
    },
    {
      name: 'teardown',
      testMatch: /.*\.teardown\.ts/,
    },
    {
      name: 'tests',
      dependencies: ['setup'],
    },
  ],
})
typescript
// cleanup.teardown.ts
import {test as teardown} from '@playwright/test'

teardown('cleanup', async ({request}) => {
  await request.delete('/api/test/data')
})

Anti-Patterns to Avoid

Anti-PatternProblemSolution
Too many browser projectsSlow CI, expensiveFocus on critical browsers
Missing setup dependenciesTests fail randomlyDeclare all dependencies explicitly
Duplicated configurationHard to maintainExtract shared config
Not using setup projectsRepeated auth in testsUse setup project + storageState