| name | playwright-generate |
| description | Generate TypeScript Playwright Test files from validated user flows. Uses Playwright MCP to explore and confirm locators before code generation. Use this skill when converting verified browser flows into regression test code, creating Playwright test suites, or generating E2E test files. Triggers on requests like "generate test code", "create Playwright tests", "convert this flow to TS", "write regression tests", or any request to produce @playwright/test TypeScript files from browser interactions. |
Playwright Generate
Playwright Test code generator that creates maintainable, production-ready TypeScript test suites from validated user flows.
Primary Goal
Convert validated user flows into @playwright/test TypeScript test files. Use Playwright MCP to explore the application first, confirm robust locators, then generate clean, well-structured test code.
Workflow
Phase 1: Exploration (Playwright MCP)
- Browser Mode: Use
playwright_* tools (headed) by default; fallback to playwright_headless_* if environment doesn't support headed mode
- Walk the Flow: Navigate through the user flow to confirm:
- Stable locators (prefer getByRole > getByLabel > getByTestId)
- Required waits and assertions
- Edge cases and error states
- Capture Evidence: Take screenshots at key steps for documentation
Phase 2: Code Generation (Playwright Test)
Only proceed with code generation when explicitly requested by the user.
Default behavior: Present a code preview/draft and await user confirmation before writing files.
Explicit triggers for auto-write:
- User says "write it" / "create the file" / "generate and save"
- User says "直接写入" / "生成并保存"
Phase 3: File Structure
Option A: Simple Test (Single Flow, <10 steps)
tests/
└── <flow-name>.spec.ts
Option B: Complex Test (Multiple Flows, Page Objects Needed)
tests/
├── <feature>.spec.ts
└── pages/
└── <page-name>.page.ts
Option C: Full Suite (Multiple Features)
tests/
├── <feature-1>.spec.ts
├── <feature-2>.spec.ts
├── pages/
│ ├── <page-1>.page.ts
│ └── <page-2>.page.ts
└── fixtures/
└── custom-fixtures.ts
Decision Criteria:
- Use Option A for quick validation tests
- Use Option B when 3+ tests share the same page interactions
- Use Option C when building a comprehensive regression suite
Phase 4: Code Quality Standards
Locator Strategy (Priority Order)
- getByRole:
page.getByRole('button', {name: 'Submit'})
- getByLabel:
page.getByLabel('Email address')
- getByPlaceholder:
page.getByPlaceholder('Enter your name')
- getByTestId:
page.getByTestId('submit-button') (only if data-testid exists)
- getByText:
page.getByText('Exact text') (use sparingly, fragile)
TypeScript Best Practices
- Use explicit types for page objects
- Prefer
async/await over promises
- Use
test.describe for grouping related tests
- Use
test.beforeEach / test.afterEach for setup/teardown
- Add meaningful test names:
test('should successfully log in with valid credentials')
Authentication Handling
Never hardcode credentials. Use one of these patterns:
Pattern 1: Storage State (Recommended)
import { test as setup } from '@playwright/test';
setup('authenticate', async ({ page }) => {
await page.goto('/login');
await page.getByLabel('Username').fill(process.env.TEST_USERNAME!);
await page.getByLabel('Password').fill(process.env.TEST_PASSWORD!);
await page.getByRole('button', {name: 'Sign In'}).click();
await page.context().storageState({ path: 'auth.json' });
});
export default defineConfig({
projects: [
{ name: 'setup', testMatch: /.*\.setup\.ts/ },
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
storageState: 'auth.json'
},
dependencies: ['setup'],
},
],
});
Pattern 2: Environment Variables
test('login flow', async ({ page }) => {
await page.getByLabel('Username').fill(process.env.TEST_USERNAME!);
await page.getByLabel('Password').fill(process.env.TEST_PASSWORD!);
});
TEST_USERNAME=testuser
TEST_PASSWORD=securepass123
Pattern 3: Fixtures (Advanced)
export const test = base.extend<{ authenticatedPage: Page }>({
authenticatedPage: async ({ page }, use) => {
await page.goto('/login');
await page.getByLabel('Username').fill(process.env.TEST_USERNAME!);
await use(page);
},
});
Assertions Strategy
- Use
expect from @playwright/test (not Jest/Chai)
- Prefer auto-waiting assertions:
toBeVisible(), toHaveText(), toBeEnabled()
- Add custom error messages:
expect(el, 'Submit button should be visible').toBeVisible()
- Use soft assertions for non-critical checks:
expect.soft(el).toContain('optional text')
Wait Strategies
await expect(page.getByRole('alert')).toBeVisible();
await page.waitForResponse(resp => resp.url().includes('/api/data'));
await page.waitForLoadState('networkidle');
Phase 5: Output Format
When presenting code (before write):
## Generated Test Plan
**File Structure**:
- tests/login.spec.ts (new)
- .env.example (new, for reference)
**Dependencies Required**:
- @playwright/test (check if installed)
- TypeScript (check if configured)
**Configuration Changes**:
- playwright.config.ts: [describe changes if needed]
---
## tests/login.spec.ts
```typescript
import { test, expect } from '@playwright/test';
test.describe('Login Flow', () => {
test('should successfully log in with valid credentials', async ({ page }) => {
// Navigate to login page
await page.goto('/login');
// Fill credentials
await page.getByLabel('Username').fill(process.env.TEST_USERNAME!);
await page.getByLabel('Password').fill(process.env.TEST_PASSWORD!);
// Submit form
await page.getByRole('button', {name: 'Sign In'}).click();
// Verify redirect and success state
await expect(page).toHaveURL('/dashboard');
await expect(page.getByRole('heading', {name: 'Dashboard'})).toBeVisible();
});
});
.env.example
TEST_USERNAME=your_username
TEST_PASSWORD=your_password
Next Steps:
- Create
.env from .env.example and add real credentials (do not commit)
- Run:
npx playwright test tests/login.spec.ts
- View report:
npx playwright show-report
Confirmation Required: Should I write these files now? (yes/no)
## Constraints & Safety
### Never Auto-Write (Ask First)
- Always present code preview before writing files
- Wait for explicit user confirmation: "write it", "create files", "save"
- Exception: User says "generate and write immediately" in the initial request
### Never Include Secrets
- Use `process.env.*` for sensitive data
- Generate `.env.example` with placeholder values
- Add warning comments about not committing secrets
### Never Run Commands Without Permission
- `bash` permission is denied by default
- If user wants auto-run tests, they must explicitly enable bash permission
- Recommend manual commands in output instead
### File Collision Handling
- Check if files already exist before writing
- If exists: Ask "File X exists. Overwrite? (yes/no)" or offer to append/merge
## Integration with playwright-verify
**Recommended Workflow**:
1. User asks `@playwright-verify` to validate a flow
2. User reviews the verification report
3. User asks `@playwright-generate` to convert the validated flow into test code
4. This agent can reference the verification report for confirmed locators and assertions
**Cross-Reference Example**:
```typescript
// Based on playwright-verify report from 2026-01-19 23:30:00
// All locators confirmed working in headed Chrome
test('login flow - regression', async ({ page }) => {
// Step 1 from verification: Navigate
await page.goto('https://app.example.com/login');
// Steps 2-3 from verification: Fill form
await page.getByLabel('Username').fill(process.env.TEST_USERNAME!);
// ...
});
Post-Generation Checklist
After writing files, verify: