Claude
Skills
Sign in
Back

playwright-fixtures-and-hooks

Included with Lifetime
$97 forever

Use when managing test state and infrastructure with reusable Playwright fixtures and lifecycle hooks for efficient test setup and teardown.

Code Review

What this skill does


# Playwright Fixtures and Hooks

Master Playwright's fixture system and lifecycle hooks to create reusable
test infrastructure, manage test state, and build maintainable test suites.
This skill covers built-in fixtures, custom fixtures, and best practices
for test setup and teardown.

## Built-in Fixtures

### Core Fixtures

```typescript
import { test, expect } from '@playwright/test';

test('using built-in fixtures', async ({
  page,      // Page instance
  context,   // Browser context
  browser,   // Browser instance
  request,   // API request context
}) => {
  // Each test gets fresh page and context
  await page.goto('https://example.com');
  await expect(page).toHaveTitle(/Example/);
});
```

### Page Fixture

```typescript
test('page fixture examples', async ({ page }) => {
  // Navigate
  await page.goto('https://example.com');

  // Interact
  await page.getByRole('button', { name: 'Click me' }).click();

  // Wait
  await page.waitForLoadState('networkidle');

  // Evaluate
  const title = await page.title();
  expect(title).toBe('Example Domain');
});
```

### Context Fixture

```typescript
test('context fixture examples', async ({ context, page }) => {
  // Add cookies
  await context.addCookies([
    {
      name: 'session',
      value: 'abc123',
      domain: 'example.com',
      path: '/',
    },
  ]);

  // Set permissions
  await context.grantPermissions(['geolocation']);

  // Create additional page in same context
  const page2 = await context.newPage();
  await page2.goto('https://example.com');

  // Both pages share cookies and storage
  await page.goto('https://example.com');
});
```

### Browser Fixture

```typescript
test('browser fixture examples', async ({ browser }) => {
  // Create custom context with options
  const context = await browser.newContext({
    viewport: { width: 1920, height: 1080 },
    locale: 'en-US',
    timezoneId: 'America/New_York',
    permissions: ['geolocation'],
  });

  const page = await context.newPage();
  await page.goto('https://example.com');

  await context.close();
});
```

### Request Fixture

```typescript
test('API testing with request fixture', async ({ request }) => {
  // Make GET request
  const response = await request.get('https://api.example.com/users');
  expect(response.ok()).toBeTruthy();
  expect(response.status()).toBe(200);

  const users = await response.json();
  expect(users).toHaveLength(10);

  // Make POST request
  const createResponse = await request.post('https://api.example.com/users', {
    data: {
      name: 'John Doe',
      email: '[email protected]',
    },
  });
  expect(createResponse.ok()).toBeTruthy();
});
```

## Custom Fixtures

### Basic Custom Fixture

```typescript
// fixtures/base-fixtures.ts
import { test as base } from '@playwright/test';

type MyFixtures = {
  timestamp: string;
};

export const test = base.extend<MyFixtures>({
  timestamp: async ({}, use) => {
    const timestamp = new Date().toISOString();
    await use(timestamp);
  },
});

export { expect } from '@playwright/test';
```

```typescript
// tests/example.spec.ts
import { test, expect } from '../fixtures/base-fixtures';

test('using custom timestamp fixture', async ({ timestamp, page }) => {
  console.log(`Test started at: ${timestamp}`);
  await page.goto('https://example.com');
});
```

### Fixture with Setup and Teardown

```typescript
import { test as base } from '@playwright/test';

type DatabaseFixtures = {
  database: Database;
};

export const test = base.extend<DatabaseFixtures>({
  database: async ({}, use) => {
    // Setup: Create database connection
    const db = await createDatabaseConnection();
    console.log('Database connected');

    // Provide fixture to test
    await use(db);

    // Teardown: Close database connection
    await db.close();
    console.log('Database closed');
  },
});
```

### Fixture Scopes: Test vs Worker

```typescript
import { test as base } from '@playwright/test';

type TestScopedFixtures = {
  uniqueId: string;
};

type WorkerScopedFixtures = {
  apiToken: string;
};

export const test = base.extend<TestScopedFixtures, WorkerScopedFixtures>({
  // Test-scoped: Created for each test
  uniqueId: async ({}, use) => {
    const id = `test-${Date.now()}-${Math.random()}`;
    await use(id);
  },

  // Worker-scoped: Created once per worker
  apiToken: [
    async ({}, use) => {
      const token = await generateApiToken();
      await use(token);
      await revokeApiToken(token);
    },
    { scope: 'worker' },
  ],
});
```

## Authentication Fixtures

### Authenticated User Fixture

```typescript
// fixtures/auth-fixtures.ts
import { test as base } from '@playwright/test';

type AuthFixtures = {
  authenticatedPage: Page;
};

export const test = base.extend<AuthFixtures>({
  authenticatedPage: async ({ browser }, use) => {
    // Create new context with authentication
    const context = await browser.newContext({
      storageState: 'auth.json',
    });

    const page = await context.newPage();
    await use(page);

    await context.close();
  },
});

export { expect } from '@playwright/test';
```

### Multiple User Roles

```typescript
// fixtures/multi-user-fixtures.ts
import { test as base } from '@playwright/test';

type UserFixtures = {
  adminPage: Page;
  userPage: Page;
  guestPage: Page;
};

export const test = base.extend<UserFixtures>({
  adminPage: async ({ browser }, use) => {
    const context = await browser.newContext({
      storageState: 'auth/admin.json',
    });
    const page = await context.newPage();
    await use(page);
    await context.close();
  },

  userPage: async ({ browser }, use) => {
    const context = await browser.newContext({
      storageState: 'auth/user.json',
    });
    const page = await context.newPage();
    await use(page);
    await context.close();
  },

  guestPage: async ({ browser }, use) => {
    const context = await browser.newContext();
    const page = await context.newPage();
    await use(page);
    await context.close();
  },
});
```

### Authentication Setup

```typescript
// auth/setup.ts
import { test as setup } from '@playwright/test';

setup('authenticate as admin', async ({ page }) => {
  await page.goto('https://example.com/login');
  await page.getByLabel('Email').fill('[email protected]');
  await page.getByLabel('Password').fill('admin123');
  await page.getByRole('button', { name: 'Login' }).click();

  await page.waitForURL('**/dashboard');

  await page.context().storageState({ path: 'auth/admin.json' });
});

setup('authenticate as user', async ({ page }) => {
  await page.goto('https://example.com/login');
  await page.getByLabel('Email').fill('[email protected]');
  await page.getByLabel('Password').fill('user123');
  await page.getByRole('button', { name: 'Login' }).click();

  await page.waitForURL('**/dashboard');

  await page.context().storageState({ path: 'auth/user.json' });
});
```

## Database Fixtures

### Test Database Fixture

```typescript
// fixtures/database-fixtures.ts
import { test as base } from '@playwright/test';
import { PrismaClient } from '@prisma/client';

type DatabaseFixtures = {
  db: PrismaClient;
  cleanDb: void;
};

export const test = base.extend<DatabaseFixtures>({
  db: [
    async ({}, use) => {
      const db = new PrismaClient();
      await use(db);
      await db.$disconnect();
    },
    { scope: 'worker' },
  ],

  cleanDb: async ({ db }, use) => {
    // Clean database before test
    await db.user.deleteMany();
    await db.product.deleteMany();
    await db.order.deleteMany();

    await use();

    // Clean database after test
    await db.user.deleteMany();
    await db.product.deleteMany();
    await db.order.deleteMany();
  },
});
```

### Seeded Data Fixture

```typescript
// fixtures/seed-fixtures.ts
import { test as base } from './database-fixtures';

type SeedFixtures = {
  testUser: User;
  testProducts: Product[];
};

export const test = base.extend<SeedFixtures>({
  testUser: async ({ db, cleanDb }, 

Related in Code Review