Files
claude-skills-reference/engineering-team/playwright-pro/templates/api/auth-headers.md
Alireza Rezvani d33d03da50 feat: add playwright-pro plugin — production-grade Playwright testing toolkit (#254)
Complete Claude Code plugin with:
- 9 skills (/pw:init, generate, review, fix, migrate, coverage, testrail, browserstack, report)
- 3 specialized agents (test-architect, test-debugger, migration-planner)
- 55 test case templates across 11 categories (auth, CRUD, checkout, search, forms, dashboard, settings, onboarding, notifications, API, accessibility)
- TestRail MCP server (TypeScript) — 8 tools for bidirectional sync
- BrowserStack MCP server (TypeScript) — 7 tools for cross-browser testing
- Smart hooks (auto-validate tests, auto-detect Playwright projects)
- 6 curated reference docs (golden rules, locators, assertions, fixtures, pitfalls, flaky tests)
- Leverages Claude Code built-ins (/batch, /debug, Explore subagent)
- Zero-config for core features; TestRail/BrowserStack via env vars
- Both TypeScript and JavaScript support throughout

Co-authored-by: Leo <leo@openclaw.ai>
2026-03-05 13:50:05 +01:00

4.7 KiB

Auth Headers Template

Tests token authentication, expired token handling, and token refresh flow.

Prerequisites

  • Valid token: {{apiToken}}
  • Expired token: {{expiredApiToken}}
  • Refresh token: {{refreshToken}}
  • API base: {{apiBaseUrl}}

TypeScript

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

test.describe('API Auth Headers', () => {
  // Happy path: valid Bearer token accepted
  test('accepts valid Bearer token', async ({ request }) => {
    const res = await request.get('{{apiBaseUrl}}/me', {
      headers: { 'Authorization': `Bearer {{apiToken}}` },
    });
    expect(res.status()).toBe(200);
    const body = await res.json();
    expect(body.id).toBeTruthy();
  });

  // Happy path: API key in header accepted
  test('accepts API key header', async ({ request }) => {
    const res = await request.get('{{apiBaseUrl}}/{{entityName}}s', {
      headers: { 'X-API-Key': '{{apiKey}}' },
    });
    expect(res.status()).toBe(200);
  });

  // Error case: no auth header returns 401
  test('returns 401 without auth header', async ({ request }) => {
    const res = await request.get('{{apiBaseUrl}}/me');
    expect(res.status()).toBe(401);
    const body = await res.json();
    expect(body.error ?? body.message).toMatch(/unauthorized|authentication required/i);
  });

  // Error case: expired token returns 401
  test('returns 401 for expired token', async ({ request }) => {
    const res = await request.get('{{apiBaseUrl}}/me', {
      headers: { 'Authorization': `Bearer {{expiredApiToken}}` },
    });
    expect(res.status()).toBe(401);
    const body = await res.json();
    expect(body.error ?? body.code).toMatch(/token.*expired|expired_token/i);
  });

  // Happy path: refresh token obtains new access token
  test('refreshes expired token and retries request', async ({ request }) => {
    // Step 1: refresh
    const refresh = await request.post('{{apiBaseUrl}}/auth/refresh', {
      data: { refresh_token: '{{refreshToken}}' },
    });
    expect(refresh.status()).toBe(200);
    const { access_token } = await refresh.json();
    expect(access_token).toBeTruthy();

    // Step 2: use new token
    const res = await request.get('{{apiBaseUrl}}/me', {
      headers: { 'Authorization': `Bearer ${access_token}` },
    });
    expect(res.status()).toBe(200);
  });

  // Error case: invalid token format returns 401
  test('returns 401 for malformed token', async ({ request }) => {
    const res = await request.get('{{apiBaseUrl}}/me', {
      headers: { 'Authorization': 'Bearer not.a.jwt' },
    });
    expect(res.status()).toBe(401);
  });

  // Edge case: token in cookie vs header
  test('accepts session cookie as auth alternative', async ({ request }) => {
    const res = await request.get('{{apiBaseUrl}}/me', {
      headers: { 'Cookie': `{{sessionCookieName}}={{sessionCookieValue}}` },
    });
    expect(res.status()).toBe(200);
  });

  // Edge case: revoked token returns 401
  test('returns 401 for revoked token', async ({ request }) => {
    const res = await request.get('{{apiBaseUrl}}/me', {
      headers: { 'Authorization': `Bearer {{revokedApiToken}}` },
    });
    expect(res.status()).toBe(401);
  });
});

JavaScript

const { test, expect } = require('@playwright/test');

test.describe('API Auth Headers', () => {
  test('accepts valid Bearer token', async ({ request }) => {
    const res = await request.get('{{apiBaseUrl}}/me', {
      headers: { 'Authorization': `Bearer {{apiToken}}` },
    });
    expect(res.status()).toBe(200);
  });

  test('returns 401 without auth header', async ({ request }) => {
    const res = await request.get('{{apiBaseUrl}}/me');
    expect(res.status()).toBe(401);
  });

  test('returns 401 for expired token', async ({ request }) => {
    const res = await request.get('{{apiBaseUrl}}/me', {
      headers: { 'Authorization': `Bearer {{expiredApiToken}}` },
    });
    expect(res.status()).toBe(401);
  });

  test('refreshes token and retries', async ({ request }) => {
    const refresh = await request.post('{{apiBaseUrl}}/auth/refresh', {
      data: { refresh_token: '{{refreshToken}}' },
    });
    const { access_token } = await refresh.json();
    const res = await request.get('{{apiBaseUrl}}/me', {
      headers: { 'Authorization': `Bearer ${access_token}` },
    });
    expect(res.status()).toBe(200);
  });
});

Variants

Variant Description
Valid Bearer 200 with user data
API key X-API-Key header accepted
No auth 401 + error message
Expired token 401 + expired error code
Token refresh New token from refresh endpoint
Malformed token 401 for non-JWT
Cookie auth Session cookie accepted
Revoked token 401 for revoked token