Skip to main content

e2e-testing

Patrones de pruebas E2E con Playwright, Page Object Model, configuración, integración CI/CD, gestión de artefactos y estrategias para pruebas inestables.

Quellinformationen

Repository
affaan-m/ECC
Letzte Quellaktivität
7. Juni 2026 um 05:26
Erkannte Sprache von SKILL.md
Nicht bestimmbar
Sterne
269.367
Forks
40.260

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
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() }) ```
Auf GitHub ansehen