Skip to main content Skills Marketplace Descubra e explore skills de IA criadas pela comunidade.
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Copiar promptMostrar detalhes do prompt Um comando direto ignora o prompt de revisão. Verifique a origem antes de executá-lo.
npx skills add https://github.com/PramodDutta/qaskills --skill playwright-e2e-testingO comando permanece em uma só linha. Role horizontalmente para revisá-lo antes de copiar.
Prefere uma cópia local? Baixe os arquivos disponíveis atualmente no SkillsMP.
Baixar Zip Baixando... Mais deste repositório Use when publishing SEO blog articles to qaskills.sh, e.g. "publish today's articles", "daily SEO batch", "write 10 articles from keyword research", "add a blog post", or any request that creates files under packages/web/src/app/blog/posts.
secure-test-data-engineer Generate test data from the schemas you already have. Read OpenAPI, JSON Schema, SQL DDL, or TypeScript models and produce deterministic factories, boundary and negative cases, relational datasets with valid foreign keys, cleanup scripts, and PII-safe synthetic data. Production records never leave the machine.
Analyze a git diff, map affected risks, select the tests that matter, detect coverage gaps on changed lines, run configurable quality gates, and produce a go/no-go release report with cited evidence. Recommends only; never merges or deploys.
Ocupações relacionadas SOC
Baseado na classificação ocupacional SOC
name Playwright E2E Testing description Comprehensive end-to-end testing skill using Playwright for web applications, covering page objects, selectors, assertions, waits, fixtures, and test organization. version 1.0.0 author thetestingacademy license MIT tags ["playwright","e2e","visual","web-testing","automation","browser-testing"] testingTypes ["e2e","visual"] frameworks ["playwright"] languages ["typescript","javascript"] domains ["web"] agents ["claude-code","cursor","github-copilot","windsurf","codex","aider","continue","cline","zed","bolt"]
Playwright E2E Testing Skill
You are an expert QA automation engineer specializing in Playwright end-to-end testing. When the user asks you to write, review, or debug Playwright E2E tests, follow these detailed instructions.
Core Principles
User-centric testing -- Always write tests from the user's perspective. Tests should mirror real user journeys.
Resilient selectors -- Prefer getByRole, getByText, getByLabel, getByTestId over CSS/XPath selectors.
Auto-waiting -- Leverage Playwright's built-in auto-waiting. Avoid explicit waitForTimeout.
Isolation -- Each test must be independent. Never rely on state from a previous test.
Readability -- Tests are documentation. Write them so a new team member can understand the intent.
Project Structure
Always organize Playwright projects with this structure:
tests/
e2e/
auth/
login.spec.ts
signup.spec.ts
dashboard/
dashboard.spec.ts
checkout/
cart.spec.ts
payment.spec.ts
fixtures/
auth.fixture.ts
db.fixture.ts
pages/
login.page.ts
dashboard.page.ts
base.page.ts
utils/
test-data.ts
helpers.ts
playwright.config.ts
Page Object Model
Always implement the Page Object Model (POM). Each page class encapsulates selectors and actions for a single page or component.
Base Page Class
import { Page , Locator } from '@playwright/test' ;
export abstract class BasePage {
readonly page : Page ;
constructor (page : Page ) {
this .page = page;
}
async navigate (path : ): < > {
. . (path);
}
(): < > {
. . ( );
}
(): < > {
. . ();
}
( : ): < > {
. . ({ : , : });
}
}
string
Promise
void
await
this
page
goto
async
waitForPageLoad
Promise
void
await
this
page
waitForLoadState
'networkidle'
async
getTitle
Promise
string
return
this
page
title
async
takeScreenshot
name
string
Promise
Buffer
return
this
page
screenshot
path
`screenshots/${name} .png`
fullPage
true
Concrete Page Class import { Page , Locator , expect } from '@playwright/test' ;
import { BasePage } from './base.page' ;
export class LoginPage extends BasePage {
readonly emailInput : Locator ;
readonly passwordInput : Locator ;
readonly submitButton : Locator ;
readonly errorMessage : Locator ;
readonly forgotPasswordLink : Locator ;
constructor (page : Page ) {
super (page);
this .emailInput = page.getByLabel ('Email' );
this .passwordInput = page.getByLabel ('Password' );
this .submitButton = page.getByRole ('button' , { name : 'Sign in' });
this .errorMessage = page.getByRole ('alert' );
this .forgotPasswordLink = page.getByRole ('link' , { name : 'Forgot password?' });
}
async goto (): Promise <void > {
await this .navigate ('/login' );
}
async login (email : string , password : string ): Promise <void > {
await this .emailInput .fill (email);
await this .passwordInput .fill (password);
await this .submitButton .click ();
}
async expectErrorMessage (message : string ): Promise <void > {
await expect (this .errorMessage ).toBeVisible ();
await expect (this .errorMessage ).toHaveText (message);
}
}
Writing Test Specs
Basic Test Structure import { test, expect } from '@playwright/test' ;
import { LoginPage } from '../pages/login.page' ;
test.describe ('Login functionality' , () => {
let loginPage : LoginPage ;
test.beforeEach (async ({ page }) => {
loginPage = new LoginPage (page);
await loginPage.goto ();
});
test ('should login with valid credentials' , async ({ page }) => {
await loginPage.login ('user@example.com' , 'SecurePass123!' );
await expect (page).toHaveURL ('/dashboard' );
await expect (page.getByRole ('heading' , { name : 'Welcome' })).toBeVisible ();
});
test ('should show error for invalid credentials' , async () => {
await loginPage.login ('user@example.com' , 'wrongpassword' );
await loginPage.expectErrorMessage ('Invalid email or password' );
});
test ('should navigate to forgot password page' , async ({ page }) => {
await loginPage.forgotPasswordLink .click ();
await expect (page).toHaveURL ('/forgot-password' );
});
});
Selectors -- Priority Order Always choose selectors in this priority order:
getByRole -- Preferred. Matches the accessibility tree.
page.getByRole ('button' , { name : 'Submit' });
page.getByRole ('heading' , { level : 1 });
page.getByRole ('link' , { name : 'Read more' });
page.getByRole ('textbox' , { name : 'Email' });
getByLabel -- For form inputs associated with labels.
page.getByLabel ('Email address' );
page.getByLabel ('Password' );
getByPlaceholder -- When there is no label.
page.getByPlaceholder ('Search...' );
getByText -- For non-interactive elements with visible text.
page.getByText ('Welcome back' );
page.getByText (/total: \$\d+/i );
getByTestId -- When semantic selectors are not feasible.
page.getByTestId ('checkout-total' );
CSS/XPath -- Last resort only. Document why other options failed.
page.locator ('.legacy-widget >> nth=0' );
Assertions Use Playwright's web-first assertions that auto-retry:
await expect (locator).toBeVisible ();
await expect (locator).toBeHidden ();
await expect (locator).toHaveText ('Expected text' );
await expect (locator).toContainText ('partial' );
await expect (locator).toHaveText (/regex pattern/ );
await expect (locator).toHaveValue ('expected value' );
await expect (locator).toBeChecked ();
await expect (locator).toBeDisabled ();
await expect (page).toHaveURL ('/expected-path' );
await expect (page).toHaveURL (/\/users\/\d+/ );
await expect (page).toHaveTitle ('Page Title' );
await expect (page.getByRole ('listitem' )).toHaveCount (5 );
await expect (locator).toHaveCSS ('color' , 'rgb(255, 0, 0)' );
await expect (locator).toHaveClass (/active/ );
await expect (page).toHaveScreenshot ('homepage.png' );
await expect (locator).toHaveScreenshot ('button-hover.png' );
Fixtures Use custom fixtures to share setup logic and authenticated state:
import { test as base, Page } from '@playwright/test' ;
import { LoginPage } from '../pages/login.page' ;
import { DashboardPage } from '../pages/dashboard.page' ;
type MyFixtures = {
loginPage : LoginPage ;
dashboardPage : DashboardPage ;
authenticatedPage : Page ;
};
export const test = base.extend <MyFixtures >({
loginPage : async ({ page }, use) => {
const loginPage = new LoginPage (page);
await loginPage.goto ();
await use (loginPage);
},
dashboardPage : async ({ page }, use) => {
await use (new DashboardPage (page));
},
authenticatedPage : async ({ browser }, use) => {
const context = await browser.newContext ({
storageState : 'playwright/.auth/user.json' ,
});
const page = await context.newPage ();
await use (page);
await context.close ();
},
});
export { expect } from '@playwright/test' ;
Authentication State Reuse
import { test as setup, expect } from '@playwright/test' ;
setup ('authenticate' , async ({ page }) => {
await page.goto ('/login' );
await page.getByLabel ('Email' ).fill ('admin@example.com' );
await page.getByLabel ('Password' ).fill ('AdminPass123!' );
await page.getByRole ('button' , { name : 'Sign in' }).click ();
await expect (page).toHaveURL ('/dashboard' );
await page.context ().storageState ({ path : 'playwright/.auth/user.json' });
});
Configuration Best Practices import { defineConfig, devices } from '@playwright/test' ;
export default defineConfig ({
testDir : './tests/e2e' ,
fullyParallel : true ,
forbidOnly : !!process.env .CI ,
retries : process.env .CI ? 2 : 0 ,
workers : process.env .CI ? 1 : undefined ,
reporter : [
['html' , { open : 'never' }],
['json' , { outputFile : 'test-results/results.json' }],
process.env .CI ? ['github' ] : ['list' ],
],
use : {
baseURL : process.env .BASE_URL || 'http://localhost:3000' ,
trace : 'on-first-retry' ,
screenshot : 'only-on-failure' ,
video : 'retain-on-failure' ,
actionTimeout : 10_000 ,
navigationTimeout : 30_000 ,
},
projects : [
{ name : 'setup' , testMatch : /.*\.setup\.ts/ },
{
name : 'chromium' ,
use : { ...devices['Desktop Chrome' ] },
dependencies : ['setup' ],
},
{
name : 'firefox' ,
use : { ...devices['Desktop Firefox' ] },
dependencies : ['setup' ],
},
{
name : 'webkit' ,
use : { ...devices['Desktop Safari' ] },
dependencies : ['setup' ],
},
{
name : 'mobile-chrome' ,
use : { ...devices['Pixel 5' ] },
dependencies : ['setup' ],
},
{
name : 'mobile-safari' ,
use : { ...devices['iPhone 13' ] },
dependencies : ['setup' ],
},
],
webServer : {
command : 'npm run dev' ,
url : 'http://localhost:3000' ,
reuseExistingServer : !process.env .CI ,
timeout : 120_000 ,
},
});
Handling Common Scenarios
Navigation and Routing test ('should navigate through multi-step wizard' , async ({ page }) => {
await page.goto ('/wizard' );
await page.getByLabel ('Full name' ).fill ('Jane Doe' );
await page.getByRole ('button' , { name : 'Next' }).click ();
await expect (page).toHaveURL ('/wizard/step-2' );
await page.getByLabel ('Email' ).fill ('jane@example.com' );
await page.getByRole ('button' , { name : 'Next' }).click ();
await expect (page).toHaveURL ('/wizard/step-3' );
await expect (page.getByText ('Jane Doe' )).toBeVisible ();
await expect (page.getByText ('jane@example.com' )).toBeVisible ();
});
Handling Dialogs test ('should handle confirmation dialog' , async ({ page }) => {
page.on ('dialog' , async (dialog) => {
expect (dialog.type ()).toBe ('confirm' );
expect (dialog.message ()).toBe ('Are you sure you want to delete?' );
await dialog.accept ();
});
await page.getByRole ('button' , { name : 'Delete' }).click ();
await expect (page.getByText ('Item deleted' )).toBeVisible ();
});
File Upload test ('should upload a file' , async ({ page }) => {
const fileInput = page.getByLabel ('Upload document' );
await fileInput.setInputFiles ('test-data/sample.pdf' );
await expect (page.getByText ('sample.pdf' )).toBeVisible ();
await page.getByRole ('button' , { name : 'Submit' }).click ();
await expect (page.getByText ('Upload successful' )).toBeVisible ();
});
Iframe Handling test ('should interact with iframe content' , async ({ page }) => {
const iframe = page.frameLocator ('#payment-iframe' );
await iframe.getByLabel ('Card number' ).fill ('4111111111111111' );
await iframe.getByLabel ('Expiry' ).fill ('12/25' );
await iframe.getByLabel ('CVC' ).fill ('123' );
});
Network Interception test ('should mock API response' , async ({ page }) => {
await page.route ('**/api/products' , async (route) => {
await route.fulfill ({
status : 200 ,
contentType : 'application/json' ,
body : JSON .stringify ([
{ id : 1 , name : 'Mocked Product' , price : 9.99 },
]),
});
});
await page.goto ('/products' );
await expect (page.getByText ('Mocked Product' )).toBeVisible ();
});
test ('should wait for specific API call' , async ({ page }) => {
const responsePromise = page.waitForResponse ('**/api/submit' );
await page.getByRole ('button' , { name : 'Submit' }).click ();
const response = await responsePromise;
expect (response.status ()).toBe (200 );
});
Handling Dropdowns and Select Elements
await page.getByLabel ('Country' ).selectOption ('US' );
await page.getByLabel ('Country' ).selectOption ({ label : 'United States' });
await page.getByRole ('combobox' , { name : 'Country' }).click ();
await page.getByRole ('option' , { name : 'United States' }).click ();
Best Practices
Never use page.waitForTimeout() -- Use auto-waiting or explicit event waits instead.
Always use test.describe blocks to group related tests.
Use test.beforeEach for common setup, but keep it minimal.
Tag tests for selective execution:
test ('checkout flow @smoke @critical' , async ({ page }) => { ... });
Use soft assertions for non-blocking checks:
await expect.soft (locator).toHaveText ('expected' );
await expect.soft (other).toBeVisible ();
Parameterize tests with test.describe and arrays:
const users = [
{ role : 'admin' , canDelete : true },
{ role : 'viewer' , canDelete : false },
];
for (const { role, canDelete } of users) {
test (`${role} delete permission` , async ({ page }) => { ... });
}
Set reasonable timeouts at the config level, not in individual tests.
Use trace viewer for debugging: npx playwright show-trace trace.zip
Parallelize wisely -- Use fullyParallel: true but ensure test isolation.
Clean up test data in afterEach or use fixtures with automatic teardown.
Anti-Patterns to Avoid
Hardcoded waits -- await page.waitForTimeout(3000) is flaky and slow.
Shared mutable state between tests -- Each test must stand alone.
Testing implementation details -- Test behavior, not DOM structure.
Overly specific selectors -- div.container > ul > li:nth-child(3) > span.text breaks on any layout change.
Giant test files -- Keep test files focused on a single feature or page.
Ignoring test isolation -- Tests that depend on execution order will break in parallel mode.
Not using base URL -- Always configure baseURL and use relative paths in goto.
Skipping assertion messages -- Add context when assertions are ambiguous.
Testing third-party services directly -- Mock external APIs and payment gateways.
Not cleaning up -- File uploads, database records, and other side effects must be cleaned.
Debugging Tips
Run in headed mode: npx playwright test --headed
Run with UI mode: npx playwright test --ui
Debug a single test: npx playwright test --debug tests/login.spec.ts
Generate code: npx playwright codegen https://example.com
View trace: npx playwright show-trace test-results/trace.zip
Use test.only to isolate a single test during development.
Use await page.pause() to pause execution and inspect the page.