# Playwright Codegen To Spec

> Convert raw Playwright codegen recordings into clean Page Object Model test specs. Use when: you have a codegen output and want to refactor it into maintainable page objects, proper fixtures, and structured test files.

- Skill: `bavithiranhardy14/playwright-codegen-to-spec` (Agent Skill)
- Install (CLI): `npx skillmds@latest add bavithiranhardy14/playwright-codegen-to-spec`
- Raw SKILL.md: https://api.skillmd.com/api/skills/bavithiranhardy14/playwright-codegen-to-spec/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: bavithiranhardy14 (https://skillmd.com/u/bavithiranhardy14)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/bavithiranhardy14/playwright-codegen-to-spec

---


# Playwright Codegen → Clean Spec

## When to Use

- You recorded a test with `playwright codegen`
- You want to convert raw recorded code into proper POM structure
- You need to clean up auto-generated selectors
- You want reusable page objects from a recording

## How to Generate a Recording

```bash
# Launch codegen — opens browser + inspector
playwright codegen https://app.example.com

# Record to a file
playwright codegen --output recorded_test.py https://app.example.com

# Record with specific browser
playwright codegen --browser firefox https://app.example.com

# Record with device emulation
playwright codegen --device "iPhone 12" https://app.example.com
```

---

## Step 1 — Typical Raw Codegen Output

```python
# recorded_test.py  ← raw codegen — DO NOT use as-is
from playwright.sync_api import Playwright, sync_playwright

def run(playwright: Playwright) -> None:
    browser = playwright.chromium.launch(headless=False)
    context = browser.new_context()
    page = context.new_page()

    page.goto("https://app.example.com/login")
    page.locator("#username").click()
    page.locator("#username").fill("admin@example.com")
    page.locator("#password").click()
    page.locator("#password").fill("password123")
    page.locator("#loginBtn").click()
    page.wait_for_url("https://app.example.com/dashboard")
    page.locator(".welcome-message").click()
    page.locator("a[href='/settings']").click()
    page.locator("#save-btn").click()

    context.close()
    browser.close()

with sync_playwright() as playwright:
    run(playwright)
```

---

## Step 2 — Copilot Converts to Page Object

Ask Copilot:
> *"Convert this codegen output into a Page Object Model with proper fixtures"*

**Generated: `pages/login_page.py`**

```python
from pages.base_page import PlaywrightBasePage
from utils.logger import get_logger

logger = get_logger(__name__)

class LoginPage(PlaywrightBasePage):
    """Generated from codegen recording — login flow."""

    # Selectors extracted from recording & improved
    USERNAME     = "#username"
    PASSWORD     = "#password"
    LOGIN_BUTTON = "#loginBtn"
    ERROR_MSG    = ".error-message"

    def login(self, username: str, password: str) -> None:
        logger.info(f"Login as: {username}")
        self.fill(self.USERNAME, username)
        self.fill(self.PASSWORD, password)
        self.click(self.LOGIN_BUTTON)
        self.wait_for_load_state("networkidle")

    def is_error_displayed(self) -> bool:
        return self.is_visible(self.ERROR_MSG)

    def get_error_text(self) -> str:
        return self.get_text(self.ERROR_MSG) if self.is_error_displayed() else ""
```

**Generated: `pages/settings_page.py`**

```python
from pages.base_page import PlaywrightBasePage
from utils.logger import get_logger

logger = get_logger(__name__)

class SettingsPage(PlaywrightBasePage):
    """Generated from codegen recording — settings flow."""

    SAVE_BUTTON    = "#save-btn"
    SUCCESS_TOAST  = ".toast-success"

    def save_settings(self) -> None:
        self.click(self.SAVE_BUTTON)
        self.wait_for_selector(self.SUCCESS_TOAST)
        logger.info("Settings saved")

    def is_save_successful(self) -> bool:
        return self.is_visible(self.SUCCESS_TOAST)
```

---

## Step 3 — Copilot Generates Clean Test Spec

**Generated: `tests/ui/test_login_flow.py`**

```python
import pytest
from pages.login_page import LoginPage
from pages.settings_page import SettingsPage
from playwright.sync_api import expect

class TestLoginFlow:
    """Test spec generated from codegen recording."""

    @pytest.fixture(autouse=True)
    def setup(self, page):
        self.page = page.page
        self.page.goto("https://app.example.com/login")

    @pytest.mark.smoke
    def test_login_redirects_to_dashboard(self):
        login = LoginPage(self.page)
        login.login("admin@example.com", "password123")

        expect(self.page).to_have_url("**/dashboard")

    @pytest.mark.regression
    def test_login_then_navigate_to_settings(self):
        login = LoginPage(self.page)
        login.login("admin@example.com", "password123")

        # Navigate to settings
        self.page.click("a[href='/settings']")

        settings = SettingsPage(self.page)
        settings.save_settings()

        assert settings.is_save_successful()

    @pytest.mark.regression
    def test_invalid_login_shows_error(self):
        login = LoginPage(self.page)
        login.login("wrong@example.com", "badpass")

        assert login.is_error_displayed()
        assert "Invalid" in login.get_error_text()
```

---

## Step 4 — Selector Upgrade Map

Copilot automatically upgrades codegen selectors:

| Raw Codegen Selector | Upgraded Selector | Why |
|---|---|---|
| `page.locator("#username")` | `page.get_by_label("Username")` | Accessible + resilient |
| `page.locator(".btn-primary")` | `page.get_by_role("button", name="Login")` | Role-based |
| `page.locator("a[href='/settings']")` | `page.get_by_role("link", name="Settings")` | Intent-based |
| `page.locator(".welcome-message")` | `page.get_by_text("Welcome")` | Text-based |
| `page.locator("#save-btn")` | `page.get_by_test_id("save-btn")` | Stable test ID |

---

## Usage

1. Run `playwright codegen <url>` and copy the output
2. Open Copilot Chat and paste the raw code
3. Ask: *"Convert this Playwright codegen output into Page Object Model with proper test spec"*
4. Copilot generates: `pages/`, `tests/`, upgraded selectors, fixtures

