Skip to main content
e2e-testing-patterns Master end-to-end testing with Playwright and Cypress to build reliable test suites that catch bugs, improve confidence, and enable fast deployment. Use when implementing E2E tests, debugging flaky tests, or establishing testing standards.
Ir para a instalação 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/blesstosam/trpc-fastify-starter --skill e2e-testing-patternsO 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... name e2e-testing-patterns description Master end-to-end testing with Playwright and Cypress to build reliable test suites that catch bugs, improve confidence, and enable fast deployment. Use when implementing E2E tests, debugging flaky tests, or establishing testing standards.
E2E Testing Patterns
Build reliable, fast, and maintainable end-to-end test suites that provide confidence to ship code quickly and catch regressions before users do.
When to Use This Skill
Implementing end-to-end test automation
Debugging flaky or unreliable tests
Testing critical user workflows
Setting up CI/CD test pipelines
Testing across multiple browsers
Validating accessibility requirements
Testing responsive designs
Establishing E2E testing standards
Core Concepts
1. E2E Testing Fundamentals
What to Test with E2E:
Critical user journeys (login, checkout, signup)
Complex interactions (drag-and-drop, multi-step forms)
Cross-browser compatibility
Real API integration
Authentication flows
What NOT to Test with E2E:
Unit-level logic (use unit tests)
API contracts (use integration tests)
Edge cases (too slow)
Internal implementation details
2. Test Philosophy
The Testing Pyramid:
/\
/E2E\ ← Few, focused on critical paths
/─────\
/Integr\ ← More, test component interactions
/────────\
/Unit Tests\ ← Many, fast, isolated
/────────────\
Best Practices:
Test user behavior, not implementation
Keep tests independent
Make tests deterministic
Optimize for speed
Use data-testid, not CSS selectors
Playwright Patterns
Setup and Configuration
import { defineConfig, devices } from "@playwright/test" ;
export default defineConfig ({
testDir : "./e2e" ,
timeout : 30000 ,
expect : {
timeout : 5000 ,
},
fullyParallel : true ,
forbidOnly : !!process.env .CI ,
retries : process.env .CI ? 2 : 0 ,
workers : process.env .CI ? 1 : undefined ,
reporter : [["html" ], ["junit" , { outputFile : "results.xml" }]],
use : {
baseURL : "http://localhost:3000" ,
trace : "on-first-retry" ,
screenshot : "only-on-failure" ,
video : "retain-on-failure" ,
},
projects : [
{ name : "chromium" , use : { ...devices["Desktop Chrome" ] } },
{ name : "firefox" , use : { ...devices["Desktop Firefox" ] } },
{ name : "webkit" , use : { ...devices["Desktop Safari" ] } },
{ name : "mobile" , use : { ...devices["iPhone 13" ] } },
],
});
Pattern 1: Page Object Model
import { Page , Locator } from "@playwright/test" ;
export class LoginPage {
readonly page : Page ;
readonly emailInput : Locator ;
readonly passwordInput : Locator ;
readonly loginButton : Locator ;
readonly errorMessage : Locator ;
constructor (page : Page ) {
this .page = page;
this .emailInput = page.getByLabel ("Email" );
this .passwordInput = page.getByLabel ("Password" );
this .loginButton = page.getByRole ("button" , { name : "Login" });
this .errorMessage = page.getByRole ("alert" );
}
async goto ( ) {
await this .page .goto ("/login" );
}
async login (email : string , password : string ) {
await this .emailInput .fill (email);
await this .passwordInput .fill (password);
await this .loginButton .click ();
}
async getErrorMessage (): Promise <string > {
return (await this .errorMessage .textContent ()) ?? "" ;
}
}
import { test, expect } from "@playwright/test" ;
import { LoginPage } from "./pages/LoginPage" ;
test ("successful login" , async ({ page }) => {
const loginPage = new LoginPage (page);
await loginPage.goto ();
await loginPage.login ("user@example.com" , "password123" );
await expect (page).toHaveURL ("/dashboard" );
await expect (page.getByRole ("heading" , { name : "Dashboard" })).toBeVisible ();
});
test ("failed login shows error" , async ({ page }) => {
const loginPage = new LoginPage (page);
await loginPage.goto ();
await loginPage.login ("invalid@example.com" , "wrong" );
const error = await loginPage.getErrorMessage ();
expect (error).toContain ("Invalid credentials" );
});
Pattern 2: Fixtures for Test Data
import { test as base } from "@playwright/test" ;
type TestData = {
testUser : {
email : string ;
password : string ;
name : string ;
};
adminUser : {
email : string ;
password : string ;
};
};
export const test = base.extend <TestData >({
testUser : async ({}, use) => {
const user = {
email : `test-${Date .now()} @example.com` ,
password : "Test123!@#" ,
name : "Test User" ,
};
await createTestUser (user);
await use (user);
await deleteTestUser (user.email );
},
adminUser : async ({}, use) => {
await use ({
email : "admin@example.com" ,
password : process.env .ADMIN_PASSWORD !,
});
},
});
import { test } from "./fixtures/test-data" ;
test ("user can update profile" , async ({ page, testUser }) => {
await page.goto ("/login" );
await page.getByLabel ("Email" ).fill (testUser.email );
await page.getByLabel ("Password" ).fill (testUser.password );
await page.getByRole ("button" , { name : "Login" }).click ();
await page.goto ("/profile" );
await page.getByLabel ("Name" ).fill ("Updated Name" );
await page.getByRole ("button" , { name : "Save" }).click ();
await expect (page.getByText ("Profile updated" )).toBeVisible ();
});
Pattern 3: Waiting Strategies
await page.waitForTimeout (3000 );
await page.waitForLoadState ("networkidle" );
await page.waitForURL ("/dashboard" );
await page.waitForSelector ('[data-testid="user-profile"]' );
await expect (page.getByText ("Welcome" )).toBeVisible ();
await expect (page.getByRole ("button" , { name : "Submit" })).toBeEnabled ();
const responsePromise = page.waitForResponse (
(response ) =>
response.url ().includes ("/api/users" ) && response.status () === 200 ,
);
await page.getByRole ("button" , { name : "Load Users" }).click ();
const response = await responsePromise;
const data = await response.json ();
expect (data.users ).toHaveLength (10 );
await Promise .all ([
page.waitForURL ("/success" ),
page.waitForLoadState ("networkidle" ),
expect (page.getByText ("Payment successful" )).toBeVisible (),
]);
Pattern 4: Network Mocking and Interception
test ("displays error when API fails" , async ({ page }) => {
await page.route ("**/api/users" , (route ) => {
route.fulfill ({
status : 500 ,
contentType : "application/json" ,
body : JSON .stringify ({ error : "Internal Server Error" }),
});
});
await page.goto ("/users" );
await expect (page.getByText ("Failed to load users" )).toBeVisible ();
});
test ("can modify API request" , async ({ page }) => {
await page.route ("**/api/users" , async (route) => {
const request = route.request ();
const postData = JSON .parse (request.postData () || "{}" );
postData.role = "admin" ;
await route.continue ({
postData : JSON .stringify (postData),
});
});
});
test ("payment flow with mocked Stripe" , async ({ page }) => {
await page.route ("**/api/stripe/**" , (route ) => {
route.fulfill ({
status : 200 ,
body : JSON .stringify ({
id : "mock_payment_id" ,
status : "succeeded" ,
}),
});
});
});
Advanced Patterns
Pattern 1: Visual Regression Testing
import { test, expect } from "@playwright/test" ;
test ("homepage looks correct" , async ({ page }) => {
await page.goto ("/" );
await expect (page).toHaveScreenshot ("homepage.png" , {
fullPage : true ,
maxDiffPixels : 100 ,
});
});
test ("button in all states" , async ({ page }) => {
await page.goto ("/components" );
const button = page.getByRole ("button" , { name : "Submit" });
await expect (button).toHaveScreenshot ("button-default.png" );
await button.hover ();
await expect (button).toHaveScreenshot ("button-hover.png" );
await button.evaluate ((el ) => el.setAttribute ("disabled" , "true" ));
await expect (button).toHaveScreenshot ("button-disabled.png" );
});
Pattern 2: Parallel Testing with Sharding
export default defineConfig ({
projects : [
{
name : "shard-1" ,
use : { ...devices["Desktop Chrome" ] },
grepInvert : /@slow/ ,
shard : { current : 1 , total : 4 },
},
{
name : "shard-2" ,
use : { ...devices["Desktop Chrome" ] },
shard : { current : 2 , total : 4 },
},
],
});
Pattern 3: Accessibility Testing
import { test, expect } from "@playwright/test" ;
import AxeBuilder from "@axe-core/playwright" ;
test ("page should not have accessibility violations" , async ({ page }) => {
await page.goto ("/" );
const accessibilityScanResults = await new AxeBuilder ({ page })
.exclude ("#third-party-widget" )
.analyze ();
expect (accessibilityScanResults.violations ).toEqual ([]);
});
test ("form is accessible" , async ({ page }) => {
await page.goto ("/signup" );
const results = await new AxeBuilder ({ page }).include ("form" ).analyze ();
expect (results.violations ).toEqual ([]);
});
Best Practices
Use Data Attributes : data-testid or data-cy for stable selectors
Avoid Brittle Selectors : Don't rely on CSS classes or DOM structure
Test User Behavior : Click, type, see - not implementation details
Keep Tests Independent : Each test should run in isolation
Clean Up Test Data : Create and destroy test data in each test
Use Page Objects : Encapsulate page logic
Meaningful Assertions : Check actual user-visible behavior
Optimize for Speed : Mock when possible, parallel execution
cy.get (".btn.btn-primary.submit-button" ).click ();
cy.get ("div > form > div:nth-child(2) > input" ).type ("text" );
cy.getByRole ("button" , { name : "Submit" }).click ();
cy.getByLabel ("Email address" ).type ("user@example.com" );
cy.get ('[data-testid="email-input"]' ).type ("user@example.com" );
Common Pitfalls
Flaky Tests : Use proper waits, not fixed timeouts
Slow Tests : Mock external APIs, use parallel execution
Over-Testing : Don't test every edge case with E2E
Coupled Tests : Tests should not depend on each other
Poor Selectors : Avoid CSS classes and nth-child
No Cleanup : Clean up test data after each test
Testing Implementation : Test user behavior, not internals
Debugging Failing Tests
npx playwright test --headed
npx playwright test --debug
await page.screenshot ({ path : 'screenshot.png' });
await page.video ()?.saveAs ('video.webm' );
test ('checkout flow' , async ({ page }) => {
await test.step ('Add item to cart' , async () => {
await page.goto ('/products' );
await page.getByRole ('button' , { name : 'Add to Cart' }).click ();
});
await test.step ('Proceed to checkout' , async () => {
await page.goto ('/cart' );
await page.getByRole ('button' , { name : 'Checkout' }).click ();
});
});
await page.pause ();
Ocupações relacionadas SOC
Baseado na classificação ocupacional SOC