Generates a complete, production-ready Playwright automation framework in TypeScript. Includes Page Object Model (POM) architecture, custom reporters, and adherence to creating robust automation guidelines. Use when the user wants to scaffold a new testing project, sets up a framework "from scratch", or requests a specific domain automation setup.
package.json with Playwright and TypeScript.pages/tests/utils/data/playwright.config.ts with robust defaults (retries, timeouts, reporters).Always enforce this directory structure for scalability:
/
├── tests/ # Test files (.spec.ts)
├── pages/ # Page Object Models
├── utils/ # Helper functions and hooks
├── fixtures/ # Test fixtures (data or custom objects)
├── reports/ # Output directory for test results
├── playwright.config.ts # Core configuration
└── package.json
playwright.config.ts TemplateGenerate a config that includes:
on-first-retry to save storage while assisting debugging.import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: [['html'], ['list']],
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'] } },
],
});
getByRole, getByText) over fragile CSS/XPath.Create in .agent/skills/scaffolding-playwright-projects/examples/base-page.ts:
import { Page, Locator } from '@playwright/test';
export class BasePage {
readonly page: Page;
constructor(page: Page) {
this.page = page;
}
async navigate(path: string) {
await this.page.goto(path);
}
async waitForNetworkIdle() {
await this.page.waitForLoadState('networkidle');
}
}
Create in .agent/skills/scaffolding-playwright-projects/resources/custom-reporter.ts:
import { Reporter, TestCase, TestResult } from '@playwright/test/reporter';
class MyReporter implements Reporter {
onTestEnd(test: TestCase, result: TestResult) {
if (result.status !== 'passed') {
console.log(`❌ Test ${test.title} failed with status: ${result.status}`);
}
}
}
export default MyReporter;