Patrones de pruebas E2E con Playwright, Page Object Model, configuración, integración CI/CD, gestión de artefactos y estrategias para pruebas inestables.
Scanned 9/4/2026
Install to Claude Code
npx -y skills add affaan-m/everything-claude-code --skill e2e-testing --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of E2e Testing?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/affaan-m-e2e-testing-ecc)More formats (shields.io, HTML) on the badges page.
---
name: e2e-testing
description: Patrones de pruebas E2E con Playwright, Page Object Model, configuración, integración CI/CD, gestión de artefactos y estrategias para pruebas inestables.
origin: ECC
---
# Patrones de Pruebas E2E
Patrones completos de Playwright para construir suites de pruebas E2E estables, rápidas y mantenibles.
## Organización de Archivos de Prueba
```
tests/
├── e2e/
│ ├── auth/
│ │ ├── login.spec.ts
│ │ ├── logout.spec.ts
│ │ └── register.spec.ts
│ ├── features/
│ │ ├── browse.spec.ts
│ │ ├── search.spec.ts
│ │ └── create.spec.ts
│ └── api/
│ └── endpoints.spec.ts
├── fixtures/
│ ├── auth.ts
│ └── data.ts
└── playwright.config.ts
```
## Page Object Model (POM)
```typescript
import { Page, Locator } from '@playwright/test'
export class ItemsPage {
readonly page: Page
readonly searchInput: Locator
readonly itemCards: Locator
readonly createButton: Locator
constructor(page: Page) {
this.page = page
this.searchInput = page.locator('[data-testid="search-input"]')
this.itemCards = page.locator('[data-testid="item-card"]')
this.createButton = page.locator('[data-testid="create-btn"]')
}
async goto() {
await this.page.goto('/items')
await this.page.waitForLoadState('networkidle')
}
async search(query: string) {
await this.searchInput.fill(query)
await this.page.waitForResponse(resp => resp.url().includes('/api/search'))
await this.page.waitForLoadState('networkidle')
}
async getItemCount() {
return await this.itemCards.count()
}
}
```
## Estructura de Pruebas
```typescript
import { test, expect } from '@playwright/test'
import { ItemsPage } from '../../pages/ItemsPage'
test.describe('Item Search', () => {
let itemsPage: ItemsPage
test.beforeEach(async ({ page }) => {
itemsPage = new ItemsPage(page)
await itemsPage.goto()
})
test('should search by keyword', async ({ page }) => {
await itemsPage.search('test')
const count = await itemsPage.getItemCount()
expect(count).toBeGreaterThan(0)
await expect(itemsPage.itemCards.first()).toContainText(/test/i)
await page.screenshot({ path: 'artifacts/search-results.png' })
})
test('should handle no results', async ({ page }) => {
await itemsPage.search('xyznonexistent123')
await expect(page.locator('[data-testid="no-results"]')).toBeVisible()
expect(await itemsPage.getItemCount()).toBe(0)
})
})
```
## Configuración de Playwright
```typescript
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', { outputFolder: 'playwright-report' }],
['junit', { outputFile: 'playwright-results.xml' }],
['json', { outputFile: 'playwright-results.json' }]
],
use: {
baseURL: process.env.BASE_URL || 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
actionTimeout: 10000,
navigationTimeout: 30000,
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
{ name: 'mobile-chrome', use: { ...devices['Pixel 5'] } },
],
webServer: {
command: 'npm run dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
timeout: 120000,
},
})
```
## Patrones de Pruebas Inestables (Flaky Tests)
### Cuarentena
```typescript
test('flaky: complex search', async ({ page }) => {
test.fixme(true, 'Flaky - Issue #123')
// código de prueba...
})
test('conditional skip', async ({ page }) => {
test.skip(process.env.CI, 'Flaky in CI - Issue #123')
// código de prueba...
})
```
### Identificar Inestabilidad
```bash
npx playwright test tests/search.spec.ts --repeat-each=10
npx playwright test tests/search.spec.ts --retries=3
```
### Causas Comunes y Soluciones
**Condiciones de carrera:**
```typescript
// Mal: asume que el elemento está listo
await page.click('[data-testid="button"]')
// Bien: locator con auto-espera
await page.locator('[data-testid="button"]').click()
```
**Timing de red:**
```typescript
// Mal: timeout arbitrario
await page.waitForTimeout(5000)
// Bien: esperar condición específica
await page.waitForResponse(resp => resp.url().includes('/api/data'))
```
**Timing de animación:**
```typescript
// Mal: hacer clic durante la animación
await page.click('[data-testid="menu-item"]')
// Bien: esperar estabilidad
await page.locator('[data-testid="menu-item"]').waitFor({ state: 'visible' })
await page.waitForLoadState('networkidle')
await page.locator('[data-testid="menu-item"]').click()
```
## Gestión de Artefactos
### Capturas de Pantalla
```typescript
await page.screenshot({ path: 'artifacts/after-login.png' })
await page.screenshot({ path: 'artifacts/full-page.png', fullPage: true })
await page.locator('[data-testid="chart"]').screenshot({ path: 'artifacts/chart.png' })
```
### Trazas
```typescript
await browser.startTracing(page, {
path: 'artifacts/trace.json',
screenshots: true,
snapshots: true,
})
// ... acciones de prueba ...
await browser.stopTracing()
```
### Video
```typescript
// En playwright.config.ts
use: {
video: 'retain-on-failure',
videosPath: 'artifacts/videos/'
}
```
## Integración CI/CD
```yaml
# .github/workflows/e2e.yml
name: E2E Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npx playwright install --with-deps
- run: npx playwright test
env:
BASE_URL: ${{ vars.STAGING_URL }}
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
```
## Plantilla de Reporte de Pruebas
```markdown
# Reporte de Pruebas E2E
**Fecha:** YYYY-MM-DD HH:MM
**Duración:** Xm Ys
**Estado:** PASANDO / FALLANDO
## Resumen
- Total: X | Pasaron: Y (Z%) | Fallaron: A | Inestables: B | Omitidas: C
## Pruebas Fallidas
### nombre-de-prueba
**Archivo:** `tests/e2e/feature.spec.ts:45`
**Error:** Expected element to be visible
**Captura:** artifacts/failed.png
**Corrección Recomendada:** [descripción]
## Artefactos
- Reporte HTML: playwright-report/index.html
- Capturas: artifacts/*.png
- Videos: artifacts/videos/*.webm
- Trazas: artifacts/*.zip
```
## Pruebas de Wallet / Web3
```typescript
test('wallet connection', async ({ page, context }) => {
// Mock del proveedor de wallet
await context.addInitScript(() => {
window.ethereum = {
isMetaMask: true,
request: async ({ method }) => {
if (method === 'eth_requestAccounts')
return ['0x1234567890123456789012345678901234567890']
if (method === 'eth_chainId') return '0x1'
}
}
})
await page.goto('/')
await page.locator('[data-testid="connect-wallet"]').click()
await expect(page.locator('[data-testid="wallet-address"]')).toContainText('0x1234')
})
```
## Pruebas de Flujos Financieros / Críticos
```typescript
test('trade execution', async ({ page }) => {
// Omitir en producción — dinero real
test.skip(process.env.NODE_ENV === 'production', 'Skip on production')
await page.goto('/markets/test-market')
await page.locator('[data-testid="position-yes"]').click()
await page.locator('[data-testid="trade-amount"]').fill('1.0')
// Verificar preview
const preview = page.locator('[data-testid="trade-preview"]')
await expect(preview).toContainText('1.0')
// Confirmar y esperar blockchain
await page.locator('[data-testid="confirm-trade"]').click()
await page.waitForResponse(
resp => resp.url().includes('/api/trade') && resp.status() === 200,
{ timeout: 30000 }
)
await expect(page.locator('[data-testid="trade-success"]')).toBeVisible()
})
```
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!