454 lines
8.7 KiB
Markdown
454 lines
8.7 KiB
Markdown
# Projects & Dependencies
|
|
|
|
## Table of Contents
|
|
|
|
1. [Project Configuration](#project-configuration)
|
|
2. [Project Dependencies](#project-dependencies)
|
|
3. [Setup Projects](#setup-projects)
|
|
4. [Filtering & Running Projects](#filtering--running-projects)
|
|
5. [Sharing Configuration](#sharing-configuration)
|
|
6. [Advanced Patterns](#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](fixtures-hooks.md#authentication-patterns).
|
|
|
|
### 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-Pattern | Problem | Solution |
|
|
| -------------------------- | ---------------------- | ----------------------------------- |
|
|
| Too many browser projects | Slow CI, expensive | Focus on critical browsers |
|
|
| Missing setup dependencies | Tests fail randomly | Declare all dependencies explicitly |
|
|
| Duplicated configuration | Hard to maintain | Extract shared config |
|
|
| Not using setup projects | Repeated auth in tests | Use setup project + storageState |
|
|
|
|
## Related References
|
|
|
|
- **Global Setup**: See [global-setup.md](global-setup.md) for globalSetup vs setup projects
|
|
- **Fixtures**: See [fixtures-hooks.md](fixtures-hooks.md) for authentication patterns
|
|
- **CI/CD**: See [ci-cd.md](../infrastructure-ci-cd/ci-cd.md) for running projects in CI
|