secondsky/claude-skillsMIT8 files

Playwright

Browser automation and E2E testing with Playwright. Auto-detects dev servers, writes clean test scripts. Test pages, fill forms, take screenshots, check responsive design, validate UX, test login flows, check links, automate any browser task. Use for cross-browser testing, visual regression, API testing, component testing in TypeScript/JavaScript and Python projects.

Specification
Skill ID
secondsky/claude-skills/playwright
Publisher
secondsky
Repository
claude-skills
Installs
0
Files
8
License
MIT
Synced
Sep 23, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

secondsky/claude-skills/playwrightInstalls these files
  • SKILL.md
  • examples/form-test.js
  • examples/link-checker.js
  • examples/responsive-test.js
  • lib/helpers.js
  • package.json
  • references/API_REFERENCE.md
  • run.js

What this skill tells the agent

Playwright - Browser Automation & E2E Testing

Expert knowledge for browser automation and end-to-end testing with Playwright - a modern cross-browser testing framework.

IMPORTANT - Path Resolution: This skill can be installed in different locations. Before executing commands, determine the skill directory based on where you loaded this SKILL.md file, and use that path in all commands. Replace $SKILL_DIR with the actual discovered path.

Common installation paths:

  • Plugin system: ~/.claude/plugins/*/playwright/skills/playwright
  • Manual global: ~/.claude/skills/playwright
  • Project-specific: <project>/.claude/skills/playwright

CRITICAL WORKFLOW - Follow These Steps

When automating browser tasks:

  1. Auto-detect dev servers - For localhost testing, ALWAYS run server detection FIRST: ``bash cd $SKILL_DIR && node -e "require('./lib/helpers').detectDevServers().then(servers => console.log(JSON.stringify(servers)))" ``
  • If 1 server found: Use it automatically, inform user
  • If multiple servers found: Ask user which one to test
  • If no servers found: Ask for URL or offer to help start dev server
  1. Write scripts to /tmp - NEVER write test files to skill directory; always use /tmp/playwright-test-*.js
  1. Use visible browser by default - Always use headless: false unless user specifically requests headless mode
  1. Parameterize URLs - Always make URLs configurable via constant at top of script
  1. Execute via run.js - Always run: cd $SKILL_DIR && node run.js /tmp/playwright-test-*.js

Quick Start

First-Time Setup

# Navigate to skill directory
cd $SKILL_DIR

# Install using bun (preferred)
bun run setup

# Or using npm
npm run setup:npm

This installs Playwright and Chromium browser. Only needed once.

Installation (For E2E Testing Projects)

# Using Bun (preferred)
bun add -d @playwright/test
bunx playwright install

# Using npm
npm init playwright@latest

Configuration

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

export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  reporter: 'html',
  use: {
    baseURL: 'http://localhost:3000',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
  },
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
    { name: 'webkit', use: { ...devices['Desktop Safari'] } },
  ],
  webServer: {
    command: 'bun run dev',
    url: 'http://localhost:3000',
  },
})

Browser Automation Patterns

How It Works

  1. You describe what you want to test/automate
  2. I auto-detect running dev servers (or ask for URL)
  3. I write custom Playwright code in /tmp/playwright-test-*.js
  4. I execute it via: cd $SKILL_DIR && node run.js /tmp/playwright-test-*.js
  5. Results displayed in real-time, browser window visible

Test a Page (Multiple Viewports)

// /tmp/playwright-test-responsive.js
const { chromium } = require('playwright');

const TARGET_URL = 'http://localhost:3001'; // Auto-detected

(async () => {
  const browser = await chromium.launch({ headless: false, slowMo: 100 });
  const page = await browser.newPage();

  // Desktop test
  await page.setViewportSize({ width: 1920, height: 1080 });
  await page.goto(TARGET_URL);
  console.log('Desktop - Title:', await page.title());
  await page.screenshot({ path: '/tmp/desktop.png', fullPage: true });

  // Mobile test
  await page.setViewportSize({ width: 375, height: 667 });
  await page.screenshot({ path: '/tmp/mobile.png', fullPage: true });

  await browser.close();
})();

Execute: cd $SKILL_DIR && node run.js /tmp/playwright-test-responsive.js

Test Login Flow

// /tmp/playwright-test-login.js
const { chromium } = require('playwright');

const TARGET_URL = 'http://localhost:3001'; // Auto-detected

(async () => {
  const browser = await chromium.launch({ headless: false });
  const page = await browser.newPage();

  await page.goto(`${TARGET_URL}/login`);
  await page.fill('input[name="email"]', 'test@example.com');
  await page.fill('input[name="password"]', 'password123');
  await page.click('button[type="submit"]');

  await page.waitForURL('**/dashboard');
  console.log('✅ Login successful, redirected to dashboard');

  await browser.close();
})();

Check for Broken Links

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch({ headless: false });
  const page = await browser.newPage();

  await page.goto('http://localhost:3000');

  const links = await page.locator('a[href^="http"]').all();
  const results = { working: 0, broken: [] };

  for (const link of links) {
    const href = await link.getAttribute('href');
    try {
      const response = await page.request.head(href);
      if (response.ok()) {
        results.working++;
      } else {
        results.broken.push({ url: href, status: response.status() });
      }
    } catch (e) {
      results.broken.push({ url: href, error: e.message });
    }
  }

  console.log(`✅ Working links: ${results.working}`);
  console.log(`❌ Broken links:`, results.broken);

  await browser.close();
})();

E2E Testing Patterns

Running Tests

# Run all tests
bunx playwright test

# Headed mode (see browser)
bunx playwright test --headed

# Specific file
bunx playwright test tests/login.spec.ts

# Debug mode
bunx playwright test --debug

# UI mode (interactive)
bunx playwright test --ui

# Specific browser
bunx playwright test --project=chromium

# Generate report
bunx playwright show-report

Writing Tests

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

test.describe('Login flow', () => {
  test('successful login', async ({ page }) => {
    await page.goto('/')
    await page.getByRole('link', { name: 'Login' }).click()
    await page.getByLabel('Email').fill('user@example.com')
    await page.getByLabel('Password').fill('password123')
    await page.getByRole('button', { name: 'Sign in' }).click()

    await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible()
  })

  test('shows error for invalid credentials', async ({ page }) => {
    await page.goto('/login')
    await page.getByLabel('Email').fill('wrong@example.com')
    await page.getByLabel('Password').fill('wrongpassword')
    await page.getByRole('button', { name: 'Sign in' }).click()

    await expect(page.getByText('Invalid credentials')).toBeVisible()
  })
})

Selectors (Best Practices)

// ✅ Role-based (recommended)
await page.getByRole('button', { name: 'Submit' })
await page.getByRole('link', { name: 'Home' })

// ✅ Text/Label
await page.getByText('Hello World')
await page.getByLabel('Email')

// ✅ Test ID (fallback)
await page.getByTestId('submit-button')

// ❌ Avoid CSS selectors (brittle)
await page.locator('.btn-primary')

Assertions

// Visibility
await expect(page.getByText('Success')).toBeVisible()
await expect(page.getByRole('button')).toBeEnabled()

// Text
await expect(page.getByRole('heading')).toHaveText('Welcome')
await expect(page.getByRole('alert')).toContainText('error')

// Attributes
await expect(page.getByRole('link')).toHaveAttribute('href', '/home')

// URL/Title
await expect(page).toHaveURL('/dashboard')
await expect(page).toHaveTitle('Dashboard')

// Count
await expect(page.getByRole('listitem')).toHaveCount(5)

Actions

// Clicking
await page.getByRole('button').click()
await page.getByText('File').dblclick()

// Typing
await page.getByLabel('Email').fill('user@example.com')
await page.getByLabel('Search').press('Enter')

// Selecting
await page.getByLabel('Country').selectOption('us')

// File Upload
await page.getByLabel('Upload').setInputFiles('path/to/file.pdf')

Network Mocking

Use this skill in a project

Open any RiverX project, search the Skills panel for secondsky/claude-skills/playwright, and install it in one click.

Use this skill