Skip to content

Sonje/playwright-api

Repository files navigation

GraphQL API Testing Template

API testing template with Playwright, TypeScript, GraphQL, and OAuth 2.0 (Authorization Code + PKCE).

Features

  • Playwright Test Runner - Powerful API testing framework
  • TypeScript - Full type safety with strict mode enabled
  • GraphQL Support - Functional GraphQL client using graphql-request
  • OAuth 2.0 + PKCE - Secure authentication flow (Azure B2C compatible)
  • Zod Validation - Environment variable validation
  • Cockatiel Retry Policy - Professional retry logic with exponential backoff for 401 errors
  • Mutation Helpers - Type-safe parametrized mutations
  • ESLint + Prettier - Code quality and formatting
  • Functional Approach - Pure functions over classes following coding guide
  • ClientError Types - Proper error handling with graphql-request types

Requirements

  • Node.js >=25.0.0
  • npm, pnpm, bun

Installation

  1. Clone the repository:
git clone <repository-url>
cd playwright-api
  1. Install dependencies:
npm install
  1. Install Playwright:
npx playwright install

Configuration

  1. Copy the example environment file:
cp .env.example .env
  1. Fill in your credentials in .env:
# GraphQL API Endpoint
GRAPHQL_ENDPOINT=https://someapp.com/graphql

# OAuth 2.0 Configuration (Azure B2C)
AUTH_URL=https://someapp.b2clogin.com/.../oauth2/v2.0/authorize
TOKEN_URL=https://someapp.b2clogin.com/.../oauth2/v2.0/token
CLIENT_ID=your-client-id-uuid
REDIRECT_URI=com.someapp://auth/
SCOPE=https://.../.../someapp.Api

# Optional: Pre-configured Access Token (for testing without OAuth)
# ACCESS_TOKEN=your_access_token_here

Project Structure

playwright-api/
├── src/
│   ├── config/
│   │   └── env.config.ts          # Environment validation (Zod)
│   ├── types/
│   │   ├── auth.types.ts          # OAuth 2.0 types
│   │   ├── graphql.types.ts       # GraphQL types
│   │   └── api.types.ts           # Common API types
│   ├── helpers/
│   │   ├── auth.helper.ts         # OAuth 2.0 + PKCE (functional)
│   │   ├── graphql.helper.ts      # GraphQL functions (query, mutation)
│   │   ├── mutation.helper.ts     # Parametrized mutation builder
│   │   └── crypto.helper.ts       # PKCE utilities
│   ├── fixtures/
│   │   └── api.fixture.ts         # Playwright test fixtures
│   └── tests/
│       └── example.spec.ts        # Example test file
├── .env.example                    # Environment template
├── .gitignore
├── .prettierrc                     # Prettier config
├── .eslintrc.json                  # ESLint config
├── playwright.config.ts            # Playwright config
├── tsconfig.json                   # TypeScript config
├── package.json
└── README.md

Running Tests

Run all tests

npm test

Run tests in debug mode

npm run test:debug

Open HTML report

npm run test:report

Run specific test file

npx playwright test src/tests/example.spec.ts

Run with headed mode (UI)

npx playwright test --headed

OAuth 2.0 Authentication Flow

This template implements OAuth 2.0 Authorization Code flow with PKCE (Proof Key for Code Exchange) for enhanced security.

Flow Overview

  1. Generate PKCE parameters - code_verifier and code_challenge
  2. Build authorization URL - Redirect user to OAuth provider
  3. Exchange code for token - Trade authorization code for access token
  4. Automatic token refresh - Refresh expired tokens automatically

Authentication Methods

Method 1: Pre-configured Token (Recommended for Testing)

For testing without full OAuth flow, set ACCESS_TOKEN in .env:

ACCESS_TOKEN=your_bearer_token_here

Method 2: Full OAuth Flow

Implement complete OAuth flow using provided helpers:

import { generatePKCEParams, getAuthorizationUrl, exchangeCodeForToken } from '@helpers/auth.helper'

const pkceParams = generatePKCEParams()
const authUrl = getAuthorizationUrl(pkceParams)
console.log('Visit:', authUrl)

const token = await exchangeCodeForToken(authorizationCode, pkceParams.codeVerifier)

Writing Tests

Basic Test Example

import { test, expect } from '@fixtures/api.fixture'

test.describe('User API Tests', () => {
    test('should fetch user data', async ({ graphql }) => {
        const query = `
            query GetUser($id: ID!) {
                user(id: $id) {
                    id
                    name
                    email
                }
            }
        `

        const variables = { id: '123' }
        const response = await graphql.query(query, variables)

        expect(response.user).toBeDefined()
        expect(response.user.id).toBe('123')
    })
})

Using Fixtures

The template provides these fixtures:

  • authToken - Access token string
  • graphql - GraphQL functions (query, mutation, rawRequest)
test('should use auth token', async ({ authToken }) => {
    expect(authToken).toBeDefined()
})

test('should query graphql', async ({ graphql }) => {
    const data = await graphql.query('{ __typename }')
    expect(data).toBeDefined()
})

Mutations Example

Direct Mutation

test('should create user', async ({ graphql }) => {
    const mutation = `
        mutation CreateUser($input: CreateUserInput!) {
            createUser(input: $input) {
                id
                name
            }
        }
    `

    const variables = {
        input: {
            name: 'John Doe',
            email: 'john@example.com',
        },
    }

    const response = await graphql.mutation(mutation, variables)
    expect(response.createUser.id).toBeDefined()
})

Parametrized Mutation with Helper

import { createMutation } from '@helpers/mutation.helper'

interface CreateUserInput {
    input: {
        name: string
        email: string
    }
}

interface CreateUserResponse {
    createUser: {
        id: string
        name: string
    }
}

const createUser = createMutation<CreateUserInput, CreateUserResponse>(`
    mutation CreateUser($input: CreateUserInput!) {
        createUser(input: $input) {
            id
            name
        }
    }
`)

test('should create user with helper', async () => {
    const response = await createUser({
        input: {
            name: 'John Doe',
            email: 'john@example.com',
        },
    })

    expect(response.createUser.id).toBeDefined()
})

Development

Type Checking

npm run type-check

Linting

npm run lint

Formatting

npm run format

Format Check

npm run format:check

GraphQL Schema & Types

Auto-generate Types (Optional)

For production use, consider using graphql-codegen to auto-generate types:

  1. Install:
npm install -D @graphql-codegen/cli @graphql-codegen/typescript
  1. Create codegen.yml:
schema: ${GRAPHQL_ENDPOINT}
documents: 'src/**/*.graphql'
generates:
    src/types/generated/graphql.ts:
        plugins:
            - typescript
            - typescript-operations
  1. Generate types:
npx graphql-codegen

Security Best Practices

  • Never commit .env file (already in .gitignore)
  • Use PKCE for OAuth flows (implemented)
  • Rotate access tokens regularly
  • Store sensitive data securely
  • Use HTTPS endpoints only
  • Validate environment variables (Zod)
  • Proper error handling with typed errors from graphql-request

Troubleshooting

Environment Validation Errors

If you see Zod validation errors on startup:

Environment validation failed:
  - GRAPHQL_ENDPOINT: Invalid url

Check your .env file and ensure all required variables are set correctly.

Authentication Errors

If authentication fails:

  1. Verify ACCESS_TOKEN in .env is valid
  2. Check token expiration
  3. Ensure OAuth credentials are correct
  4. Verify network/VPN access to auth endpoints

TypeScript Path Aliases Not Working

If imports like @helpers/* don't resolve:

  1. Restart your IDE/editor
  2. Run npm run type-check to verify
  3. Check tsconfig.json paths configuration

Resources

License

MIT


Happy Testing!

About

Playwright GraphQL Api testing template

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published