Tools: Train Your AI Agent to Write Production-Ready Playwright Tests

Tools: Train Your AI Agent to Write Production-Ready Playwright Tests

Source: Dev.to

We've been building end-to-end tests with AI coding agents lately, and the results have been all over the place. Fragile selectors. Broken authentication flows. Tests that pass locally but completely break in CI. Here's what I figured out: AI agents lack context about your specific application. Skills solve this by giving agents structured reference guides with proven patterns instead of making them guess. After working across multiple Playwright projects and talking to testing experts, we built a comprehensive skill set covering auth flows, visual testing, CI setup, and test scaling. Simple idea: better context in, better code out. What Is a Playwright Skill? A Playwright Skill is a curated collection of markdown guides that teach AI coding agents (and humans) how to write production-grade Playwright tests. Here's the problem it solves. Playwright's official documentation is excellent, but it's spread across dozens of pages. When you ask an AI agent like Claude Code or GitHub Copilot to write tests, it pulls from general training data. The output works for tutorials but falls apart on real sites. A Skill changes this. It gives the AI agent a structured, battle-tested reference. Instead of guessing which locator strategy to use or how to handle auth flows, the agent reads the relevant guide and produces code following patterns proven in production. The Playwright Skill isn't just for AI agents. Every guide is written in plain markdown. Human developers can read them, bookmark them, and use them as a cheat sheet. Think of it as a testing knowledge base that works for both humans and machines. Get the Playwright Skill Install 70+ production-tested skills with a single command: Getting Started: Installation Prerequisite: You'll need an AI coding agent like Claude Code, Cursor, Windsurf, or GitHub Copilot installed. You can also clone or fork the repository directly: Step 2: Understand the SKILL.md metadata The root contains a SKILL.md file. This is the metadata that AI coding agents read to understand what the skill covers. It tells the agent which guides are available, what topics they cover, and how to find them. Step 3: Use it with Claude Code
Once installed, Claude Code automatically loads relevant guides when you write tests: We tested this on a live e-commerce store, and the difference was massive. Without the Skill, Claude Code generated tutorial-quality code with brittle CSS selectors. With the Skill loaded, it used getByRole() locators, proper wait strategies, and structured test patterns that actually passed against a real site. Step 4: Customize for your team
The repo is MIT licensed. Fork it and make it yours. Add your team's naming conventions, remove guides for frameworks you don't use, or add new guides for your internal tools. Core Skill Pack (46 Guides) The core/ folder contains 46 guides covering every pattern you need for day-to-day Playwright testing: locators, assertions-and-waiting, fixtures-and-hooks, configuration, test-organization, authentication, api-testing, network-mocking, forms-and-validation, visual-regression, accessibility, component-testing, mobile-and-responsive, debugging, error-index, flaky-tests, common-pitfalls, and framework-specific guides for Next.js, React, Vue, and Angular. Example: Locator priority The locators.md guide teaches the locator order that makes tests resilient to DOM changes: Role-based locators survive redesigns. CSS selectors break the moment a developer renames a class. Example: Authentication reuse The authentication.md guide shows how to log in once and share the session across all tests: Every test starts logged in. No repeated login flows. No wasted time. Playwright CLI Skill Pack (10 Guides) Microsoft built playwright-cli specifically for AI coding agents because it's far more token-efficient than MCP-based approaches. Example: Snapshot-based automation Instead of streaming the full accessibility tree to an AI agent, the CLI saves a snapshot with short element references: Page Object Model Pack (2 Guides) Example: A basic Page Object CI/CD Skill Pack (9 Guides) Example: GitHub Actions workflow Example: Sharding across machines Migration Pack (2 Guides) Teams are moving to Playwright from Selenium or Cypress. If you're still deciding, check out the Selenium VS Cypress VS Playwright comparison. Cypress to Playwright: Quick Reference Selenium to Playwright: Quick Reference The biggest shift for Selenium users is auto-waiting. Playwright handles element readiness automatically, so most explicit waits can be removed entirely. Integrate TestDino for Real-Time Streaming TestDino is a Playwright-first AI reporting platform with real-time result streaming, AI-powered failure categorization (Bug, Flaky, or UI Change), and GitHub PR integration. Results stream to the TestDino Dashboard in real time. For detailed setup, see our full TestDino integration guide. Read the complete guide here Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse COMMAND_BLOCK:
bash ## The Repository Structure The [Playwright Skill](https://github.com/testdino-hq/playwright-skill) is an open-source repository maintained by TestDino. It's MIT licensed. Use it however you want. Fork it. Customize it. Share it with your team. The repo contains 70+ guides organized into five skill packs: playwright-skill/
├── core/ # 46 guides. The foundation.
├── playwright-cli/ # 11 guides. CLI browser automation.
├── pom/ # 2 guides. Page Object Model patterns.
├── ci/ # 9 guides. CI/CD pipelines.
├── migration/ # 2 guides. Moving from Cypress or Selenium.
├── LICENSE # MIT
├── README.md
└── SKILL.md # Metadata for AI agent loading Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
bash ## The Repository Structure The [Playwright Skill](https://github.com/testdino-hq/playwright-skill) is an open-source repository maintained by TestDino. It's MIT licensed. Use it however you want. Fork it. Customize it. Share it with your team. The repo contains 70+ guides organized into five skill packs: playwright-skill/
├── core/ # 46 guides. The foundation.
├── playwright-cli/ # 11 guides. CLI browser automation.
├── pom/ # 2 guides. Page Object Model patterns.
├── ci/ # 9 guides. CI/CD pipelines.
├── migration/ # 2 guides. Moving from Cypress or Selenium.
├── LICENSE # MIT
├── README.md
└── SKILL.md # Metadata for AI agent loading COMMAND_BLOCK:
bash ## The Repository Structure The [Playwright Skill](https://github.com/testdino-hq/playwright-skill) is an open-source repository maintained by TestDino. It's MIT licensed. Use it however you want. Fork it. Customize it. Share it with your team. The repo contains 70+ guides organized into five skill packs: playwright-skill/
├── core/ # 46 guides. The foundation.
├── playwright-cli/ # 11 guides. CLI browser automation.
├── pom/ # 2 guides. Page Object Model patterns.
├── ci/ # 9 guides. CI/CD pipelines.
├── migration/ # 2 guides. Moving from Cypress or Selenium.
├── LICENSE # MIT
├── README.md
└── SKILL.md # Metadata for AI agent loading COMMAND_BLOCK:
bash # Install all 70+ guides
npx skills add testdino-hq/playwright-skill # Or install individual packs
npx skills add testdino-hq/playwright-skill/core
npx skills add testdino-hq/playwright-skill/ci
npx skills add testdino-hq/playwright-skill/playwright-cli
npx skills add testdino-hq/playwright-skill/pom
npx skills add testdino-hq/playwright-skill/migration Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
bash # Install all 70+ guides
npx skills add testdino-hq/playwright-skill # Or install individual packs
npx skills add testdino-hq/playwright-skill/core
npx skills add testdino-hq/playwright-skill/ci
npx skills add testdino-hq/playwright-skill/playwright-cli
npx skills add testdino-hq/playwright-skill/pom
npx skills add testdino-hq/playwright-skill/migration COMMAND_BLOCK:
bash # Install all 70+ guides
npx skills add testdino-hq/playwright-skill # Or install individual packs
npx skills add testdino-hq/playwright-skill/core
npx skills add testdino-hq/playwright-skill/ci
npx skills add testdino-hq/playwright-skill/playwright-cli
npx skills add testdino-hq/playwright-skill/pom
npx skills add testdino-hq/playwright-skill/migration CODE_BLOCK:
bash git clone https://github.com/testdino-hq/playwright-skill.git Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
bash git clone https://github.com/testdino-hq/playwright-skill.git CODE_BLOCK:
bash git clone https://github.com/testdino-hq/playwright-skill.git CODE_BLOCK:
typescript // Most stable to least stable
page.getByRole('button', { name: 'Submit' }) // 1. Role-based
page.getByText('Welcome back') // 2. Text-based
page.getByLabel('Email address') // 3. Label-based
page.getByTestId('login-form') // 4. Test ID
page.locator('.submit-btn') // 5. CSS (last resort) Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
typescript // Most stable to least stable
page.getByRole('button', { name: 'Submit' }) // 1. Role-based
page.getByText('Welcome back') // 2. Text-based
page.getByLabel('Email address') // 3. Label-based
page.getByTestId('login-form') // 4. Test ID
page.locator('.submit-btn') // 5. CSS (last resort) CODE_BLOCK:
typescript // Most stable to least stable
page.getByRole('button', { name: 'Submit' }) // 1. Role-based
page.getByText('Welcome back') // 2. Text-based
page.getByLabel('Email address') // 3. Label-based
page.getByTestId('login-form') // 4. Test ID
page.locator('.submit-btn') // 5. CSS (last resort) CODE_BLOCK:
typescript async function globalSetup() { const browser = await chromium.launch(); const page = await browser.newPage(); await page.goto('https://yourapp.com/login'); await page.getByLabel('Email').fill('[email protected]'); await page.getByLabel('Password').fill('password'); await page.getByRole('button', { name: 'Sign in' }).click(); await page.context().storageState({ path: './auth.json' }); await browser.close();
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
typescript async function globalSetup() { const browser = await chromium.launch(); const page = await browser.newPage(); await page.goto('https://yourapp.com/login'); await page.getByLabel('Email').fill('[email protected]'); await page.getByLabel('Password').fill('password'); await page.getByRole('button', { name: 'Sign in' }).click(); await page.context().storageState({ path: './auth.json' }); await browser.close();
} CODE_BLOCK:
typescript async function globalSetup() { const browser = await chromium.launch(); const page = await browser.newPage(); await page.goto('https://yourapp.com/login'); await page.getByLabel('Email').fill('[email protected]'); await page.getByLabel('Password').fill('password'); await page.getByRole('button', { name: 'Sign in' }).click(); await page.context().storageState({ path: './auth.json' }); await browser.close();
} COMMAND_BLOCK:
bash # Open a browser and take a snapshot
playwright-cli open https://storedemo.testdino.com/
playwright-cli snapshot # Interact using element references from the snapshot
playwright-cli click e15
playwright-cli fill e5 "[email protected]"
playwright-cli press Enter
playwright-cli screenshot Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
bash # Open a browser and take a snapshot
playwright-cli open https://storedemo.testdino.com/
playwright-cli snapshot # Interact using element references from the snapshot
playwright-cli click e15
playwright-cli fill e5 "[email protected]"
playwright-cli press Enter
playwright-cli screenshot COMMAND_BLOCK:
bash # Open a browser and take a snapshot
playwright-cli open https://storedemo.testdino.com/
playwright-cli snapshot # Interact using element references from the snapshot
playwright-cli click e15
playwright-cli fill e5 "[email protected]"
playwright-cli press Enter
playwright-cli screenshot CODE_BLOCK:
typescript // pages/LoginPage.ts
export class LoginPage { constructor(private page: Page) {} async login(email: string, password: string) { await this.page.getByLabel('Email').fill(email); await this.page.getByLabel('Password').fill(password); await this.page.getByRole('button', { name: 'Sign in' }).click(); }
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
typescript // pages/LoginPage.ts
export class LoginPage { constructor(private page: Page) {} async login(email: string, password: string) { await this.page.getByLabel('Email').fill(email); await this.page.getByLabel('Password').fill(password); await this.page.getByRole('button', { name: 'Sign in' }).click(); }
} CODE_BLOCK:
typescript // pages/LoginPage.ts
export class LoginPage { constructor(private page: Page) {} async login(email: string, password: string) { await this.page.getByLabel('Email').fill(email); await this.page.getByLabel('Password').fill(password); await this.page.getByRole('button', { name: 'Sign in' }).click(); }
} CODE_BLOCK:
yaml name: Playwright Tests
on: [push, pull_request]
jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 - run: npm ci - run: npx playwright install --with-deps - run: npx playwright test - uses: actions/upload-artifact@v4 if: always() with: name: playwright-report path: playwright-report/ Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
yaml name: Playwright Tests
on: [push, pull_request]
jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 - run: npm ci - run: npx playwright install --with-deps - run: npx playwright test - uses: actions/upload-artifact@v4 if: always() with: name: playwright-report path: playwright-report/ CODE_BLOCK:
yaml name: Playwright Tests
on: [push, pull_request]
jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 - run: npm ci - run: npx playwright install --with-deps - run: npx playwright test - uses: actions/upload-artifact@v4 if: always() with: name: playwright-report path: playwright-report/ CODE_BLOCK:
yaml strategy: matrix: shard: [1/4, 2/4, 3/4, 4/4]
steps: - run: npx playwright test --shard=${{ matrix.shard }} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
yaml strategy: matrix: shard: [1/4, 2/4, 3/4, 4/4]
steps: - run: npx playwright test --shard=${{ matrix.shard }} CODE_BLOCK:
yaml strategy: matrix: shard: [1/4, 2/4, 3/4, 4/4]
steps: - run: npx playwright test --shard=${{ matrix.shard }} CODE_BLOCK:
javascript // Navigate to a page
cy.visit('/page') → await page.goto('/page') // Click an element
cy.get('.btn').click() → await page.locator('.btn').click() // Find text
cy.contains('Submit') → await page.getByText('Submit') // Intercept API calls
cy.intercept('GET', '/api/*') → await page.route('**/api/*', handler) // Wait for API response
cy.wait('@alias') → await page.waitForResponse('**/api/*') Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
javascript // Navigate to a page
cy.visit('/page') → await page.goto('/page') // Click an element
cy.get('.btn').click() → await page.locator('.btn').click() // Find text
cy.contains('Submit') → await page.getByText('Submit') // Intercept API calls
cy.intercept('GET', '/api/*') → await page.route('**/api/*', handler) // Wait for API response
cy.wait('@alias') → await page.waitForResponse('**/api/*') CODE_BLOCK:
javascript // Navigate to a page
cy.visit('/page') → await page.goto('/page') // Click an element
cy.get('.btn').click() → await page.locator('.btn').click() // Find text
cy.contains('Submit') → await page.getByText('Submit') // Intercept API calls
cy.intercept('GET', '/api/*') → await page.route('**/api/*', handler) // Wait for API response
cy.wait('@alias') → await page.waitForResponse('**/api/*') CODE_BLOCK:
javascript // Find an element
driver.findElement(By.id("email")) → page.getByLabel('Email') // Navigate to URL
driver.get(url) → await page.goto(url) // Explicit waits
WebDriverWait + ExpectedConditions → Built-in auto-waiting (no explicit waits needed) // Set window size
driver.manage().window().setSize() → Configured in playwright.config.ts Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
javascript // Find an element
driver.findElement(By.id("email")) → page.getByLabel('Email') // Navigate to URL
driver.get(url) → await page.goto(url) // Explicit waits
WebDriverWait + ExpectedConditions → Built-in auto-waiting (no explicit waits needed) // Set window size
driver.manage().window().setSize() → Configured in playwright.config.ts CODE_BLOCK:
javascript // Find an element
driver.findElement(By.id("email")) → page.getByLabel('Email') // Navigate to URL
driver.get(url) → await page.goto(url) // Explicit waits
WebDriverWait + ExpectedConditions → Built-in auto-waiting (no explicit waits needed) // Set window size
driver.manage().window().setSize() → Configured in playwright.config.ts CODE_BLOCK:
bash npm install @testdino/playwright Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
bash npm install @testdino/playwright CODE_BLOCK:
bash npm install @testdino/playwright COMMAND_BLOCK:
bash# Replace npx playwright test with:
npx tdpw test -t "your-api-token" Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
bash# Replace npx playwright test with:
npx tdpw test -t "your-api-token" COMMAND_BLOCK:
bash# Replace npx playwright test with:
npx tdpw test -t "your-api-token" CODE_BLOCK:
yaml - name: Run Playwright Tests run: npx tdpw test --project=desktop-chrome env: TESTDINO_TOKEN: ${{ secrets.TESTDINO_API_KEY }} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
yaml - name: Run Playwright Tests run: npx tdpw test --project=desktop-chrome env: TESTDINO_TOKEN: ${{ secrets.TESTDINO_API_KEY }} CODE_BLOCK:
yaml - name: Run Playwright Tests run: npx tdpw test --project=desktop-chrome env: TESTDINO_TOKEN: ${{ secrets.TESTDINO_API_KEY }} - "Write E2E tests for my login page" triggers Claude to read authentication.md, locators.md, and assertions-and-waiting.md
- "Set up GitHub Actions for Playwright" pulls in ci-github-actions.md and parallel-and-sharding.md
- "Migrate my Cypress tests" loads from-cypress.md