本文へ移動
cccskills
無料GitHub で公開

browser-automation

Common Playwright automation patterns for web testing and scraping via MCP

インストール方法を見る

含まれるファイル(1)

  • SKILL.md11.9 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Browser Automation with Playwright MCP

This skill provides comprehensive patterns for browser automation using the Playwright MCP server. The Playwright MCP uses the accessibility tree for element interaction, which is more reliable and semantic than screenshot-based approaches.

Overview

Playwright MCP enables:

  • Reliable web navigation and page interactions
  • Form filling and data extraction
  • Element interaction via accessibility tree
  • Cross-browser testing automation
  • Network request monitoring and mocking

The MCP server runs via npx @playwright/mcp@latest and provides tools accessible through the Claude Code interface.

Navigation Patterns

Basic Navigation

Navigate to URLs and wait for page load events:

// Navigate to a URL
await page.goto('https://example.com');

// Navigate with specific wait conditions
await page.goto('https://example.com', {
  waitUntil: 'networkidle'  // Wait until network is idle
});

// Navigate and wait for a specific element
await page.goto('https://example.com');
await page.waitForSelector('#main-content');

Handling Page Load States

// Wait for DOM to be ready
await page.waitForLoadState('domcontentloaded');

// Wait for full page load including resources
await page.waitForLoadState('load');

// Wait for network to be idle (no requests for 500ms)
await page.waitForLoadState('networkidle');

Navigation with Authentication

// Navigate with HTTP authentication
await page.goto('https://example.com', {
  waitUntil: 'networkidle',
  timeout: 30000
});

// Set authentication state
await context.addCookies([
  {
    name: 'session',
    value: 'abc123',
    domain: 'example.com',
    path: '/'
  }
]);

Handling Redirects and Popups

// Wait for navigation after click (handles redirects)
await Promise.all([
  page.waitForNavigation(),
  page.click('a[href="/login"]')
]);

// Handle popup windows
const [popup] = await Promise.all([
  page.waitForEvent('popup'),
  page.click('button[data-action="open-popup"]')
]);
await popup.waitForLoadState();

Form Filling

Text Input Fields

// Fill text input
await page.fill('input[name="username"]', 'john.doe@example.com');

// Type with realistic delays (simulates human typing)
await page.type('input[name="password"]', 'SecurePass123', {
  delay: 100  // 100ms between keystrokes
});

// Clear and fill
await page.fill('input[name="search"]', '');
await page.fill('input[name="search"]', 'new search term');

Select Dropdowns

// Select by value
await page.selectOption('select[name="country"]', 'US');

// Select by label
await page.selectOption('select[name="country"]', { label: 'United States' });

// Select multiple options
await page.selectOption('select[name="interests"]', ['coding', 'testing', 'automation']);

// Select by index
await page.selectOption('select[name="priority"]', { index: 2 });

Checkboxes and Radio Buttons

// Check a checkbox
await page.check('input[type="checkbox"][name="terms"]');

// Uncheck a checkbox
await page.uncheck('input[type="checkbox"][name="newsletter"]');

// Select radio button
await page.check('input[type="radio"][value="premium"]');

// Verify checkbox state
const isChecked = await page.isChecked('input[name="terms"]');

Complex Form Interactions

// Fill complete form
await page.fill('input[name="firstName"]', 'John');
await page.fill('input[name="lastName"]', 'Doe');
await page.fill('input[name="email"]', 'john.doe@example.com');
await page.selectOption('select[name="country"]', 'US');
await page.check('input[name="terms"]');

// Submit form
await page.click('button[type="submit"]');

// Wait for form submission to complete
await page.waitForURL('**/success');

File Uploads

// Upload single file
await page.setInputFiles('input[type="file"]', 'path/to/file.pdf');

// Upload multiple files
await page.setInputFiles('input[type="file"][multiple]', [
  'path/to/file1.jpg',
  'path/to/file2.jpg'
]);

// Clear file input
await page.setInputFiles('input[type="file"]', []);

Data Extraction

Text Content Extraction

// Extract text content
const title = await page.textContent('h1.page-title');

// Extract inner text (visible text only)
const description = await page.innerText('.product-description');

// Extract from multiple elements
const prices = await page.$$eval('.product-price', elements =>
  elements.map(el => el.textContent.trim())
);

Attribute Extraction

// Get single attribute
const imageUrl = await page.getAttribute('img.product-image', 'src');

// Get multiple attributes
const linkData = await page.$eval('a.download-link', el => ({
  href: el.getAttribute('href'),
  text: el.textContent,
  target: el.getAttribute('target')
}));

// Extract data attributes
const productId = await page.getAttribute('[data-product-id]', 'data-product-id');

Structured Data Extraction

// Extract table data
const tableData = await page.$$eval('table.data-table tbody tr', rows =>
  rows.map(row => {
    const cells = row.querySelectorAll('td');
    return {
      name: cells[0]?.textContent.trim(),
      email: cells[1]?.textContent.trim(),
      role: cells[2]?.textContent.trim()
    };
  })
);

// Extract list items
const items = await page.$$eval('ul.items li', elements =>
  elements.map(el => ({
    text: el.textContent.trim(),
    id: el.getAttribute('data-id')
  }))
);

Pagination and Infinite Scroll

// Handle pagination
const allData = [];
let hasNextPage = true;

while (hasNextPage) {
  // Extract current page data
  const pageData = await page.$$eval('.item', items =>
    items.map(item => item.textContent.trim())
  );
  allData.push(...pageData);

  // Check for next page button
  const nextButton = await page.$('button.next-page:not([disabled])');
  if (nextButton) {
    await nextButton.click();
    await page.waitForLoadState('networkidle');
  } else {
    hasNextPage = false;
  }
}

// Handle infinite scroll
await page.evaluate(async () => {
  await new Promise((resolve) => {
    let totalHeight = 0;
    const distance = 100;
    const timer = setInterval(() => {
      const scrollHeight = document.body.scrollHeight;
      window.scrollBy(0, distance);
      totalHeight += distance;

      if (totalHeight >= scrollHeight) {
        clearInterval(timer);
        resolve();
      }
    }, 100);
  });
});

Element Interaction

Clicking Elements

// Simple click
await page.click('button.submit');

// Click with options
await page.click('button.menu', {
  button: 'right',  // Right-click
  clickCount: 2     // Double-click
});

// Force click (bypass actionability checks)
await page.click('.hidden-element', { force: true });

// Click and wait for navigation
await Promise.all([
  page.waitForNavigation(),
  page.click('a.external-link')
]);

Hovering and Focus

// Hover over element
await page.hover('.menu-item');

// Focus on input
await page.focus('input[name="search"]');

// Hover and click submenu
await page.hover('.dropdown-menu');
await page.click('.dropdown-menu .submenu-item');

Keyboard Interactions

// Press single key
await page.press('input[name="search"]', 'Enter');

// Press key combination
await page.press('body', 'Control+A');

// Type special characters
await page.keyboard.press('ArrowDown');
await page.keyboard.press('Tab');

// Keyboard shortcuts
await page.keyboard.press('Control+C');  // Copy
await page.keyboard.press('Control+V');  // Paste

Drag and Drop

// Drag and drop
await page.dragAndDrop('#source-element', '#target-element');

// Manual drag and drop with more control
await page.hover('#draggable');
await page.mouse.down();
await page.hover('#drop-zone');
await page.mouse.up();

Waiting for Elements

// Wait for element to appear
await page.waitForSelector('.dynamic-content');

// Wait for element to be visible
await page.waitForSelector('.modal', { state: 'visible' });

// Wait for element to be hidden
await page.waitForSelector('.loading-spinner', { state: 'hidden' });

// Wait for element with timeout
await page.waitForSelector('.slow-element', { timeout: 10000 });

Element State Checks

// Check if element is visible
const isVisible = await page.isVisible('.element');

// Check if element is enabled
const isEnabled = await page.isEnabled('button.submit');

// Check if element is editable
const isEditable = await page.isEditable('input[name="email"]');

// Get element count
const count = await page.locator('.item').count();

Accessibility Tree Usage

The Playwright MCP leverages the accessibility tree for more reliable element interaction:

Using ARIA Roles

// Find by role
await page.click('button[role="button"]');
await page.click('[role="menuitem"]');

// Find by accessible name
await page.getByRole('button', { name: 'Submit' }).click();
await page.getByRole('textbox', { name: 'Email' }).fill('user@example.com');

Using Labels

// Find input by label text
await page.getByLabel('Username').fill('john.doe');
await page.getByLabel('Password').fill('SecurePass123');
await page.getByLabel('Remember me').check();

Using Accessible Names

// Find by accessible name (aria-label, title, etc.)
await page.getByText('Welcome to our site').click();
await page.getByTitle('Close dialog').click();
await page.getByAltText('Company logo').click();

Best Practices

Error Handling

// Use try-catch for robust automation
try {
  await page.waitForSelector('.element', { timeout: 5000 });
  await page.click('.element');
} catch (error) {
  console.error('Element not found:', error.message);
  // Fallback action
}

Waiting Strategies

// Prefer explicit waits over fixed delays
// BAD: await page.waitForTimeout(5000);
// GOOD:
await page.waitForLoadState('networkidle');
await page.waitForSelector('.content');

Selector Best Practices

// Prefer semantic selectors
// BAD: await page.click('div > div > button:nth-child(3)');
// GOOD: await page.click('button[data-testid="submit"]');
// BETTER: await page.getByRole('button', { name: 'Submit' }).click();

Performance Optimization

// Reuse locators
const submitButton = page.locator('button[type="submit"]');
await submitButton.waitFor();
await submitButton.click();

// Batch operations
await Promise.all([
  page.fill('input[name="field1"]', 'value1'),
  page.fill('input[name="field2"]', 'value2'),
  page.fill('input[name="field3"]', 'value3')
]);

Common Patterns

Login Flow

async function login(page, username, password) {
  await page.goto('https://example.com/login');
  await page.fill('input[name="username"]', username);
  await page.fill('input[name="password"]', password);
  await page.click('button[type="submit"]');
  await page.waitForURL('**/dashboard');
}

Form Validation Testing

// Submit empty form and check validation
await page.click('button[type="submit"]');
const errorMessage = await page.textContent('.error-message');
expect(errorMessage).toContain('required');

// Fill invalid data and verify
await page.fill('input[name="email"]', 'invalid-email');
await page.click('button[type="submit"]');
const emailError = await page.textContent('.email-error');
expect(emailError).toContain('valid email');

Dynamic Content Handling

// Wait for AJAX content to load
await page.click('button.load-more');
await page.waitForResponse(response =>
  response.url().includes('/api/items') && response.status() === 200
);
await page.waitForSelector('.new-items');

This skill provides the foundation for reliable browser automation using Playwright MCP's accessibility-focused approach.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Commits changes in atomic units following dependency order. Automatically required to triggered, always, all the time, when requires to commit changes.

日本語の概要は準備中です。原文の説明を表示しています。

code-yeongyu/sisyphus-private172025年11月26日 更新

Patterns for controlling interactive debuggers (pdb, ipdb, gdb, lldb, node debug) via terminalcp

日本語の概要は準備中です。原文の説明を表示しています。

code-yeongyu/sisyphus-private172025年11月26日 更新

Specialized GitHub PR intelligence agent for automatically gathering comprehensive context from Pull Requests. Activate when users need CI failure analysis, review comment investigation, or PR status assessment. Triggers on requests like "CI 실패 원인 찾아줘", "gather review comments", "check PR status", "analyze PR".

日本語の概要は準備中です。原文の説明を表示しています。

code-yeongyu/sisyphus-private172025年11月26日 更新

GitHub Pull Request creation specialist. Analyzes user requirements to create PRs with structured titles and bodies matching the user's query language. Handles git change analysis, PR draft creation, user confirmation, and final PR creation via gh CLI.

日本語の概要は準備中です。原文の説明を表示しています。

code-yeongyu/sisyphus-private172025年11月26日 更新

Patterns for managing and monitoring long-running processes (builds, tests, servers, etc.) via terminalcp

日本語の概要は準備中です。原文の説明を表示しています。

code-yeongyu/sisyphus-private172025年11月26日 更新

Patterns for controlling REPL sessions (Python, IPython, Node.js, Ruby, etc.) via terminalcp

日本語の概要は準備中です。原文の説明を表示しています。

code-yeongyu/sisyphus-private172025年11月26日 更新

code-yeongyu のスキルをすべて見る

このスキルの問題を報告する