Generador de Tests Playwright
Genera tests E2E de producción con Playwright a partir de una historia de usuario, URL, componente o descripción de feature, siguiendo buenas prácticas de locators y aserciones web-first. Incluye patrones reutilizables para auth, CRUD, formularios, búsqueda, navegación y mocking de APIs.
Descarga abierta · sin registro · para Playwright, TypeScript, Node.js
/**
- Leadspark CRM — Tests E2E con Playwright
- Cliente: Leadspark CRM (SaaS B2B gestión de leads)
- Generado por: generar-tests-playwright (CULTIVA IA)
- Cobertura:
- Autenticación (login / logout)
- CRUD de leads (crear, editar, eliminar, buscar)
- Pipeline Kanban (drag & drop, filtro por propietario)
- Dashboard con API mocking (éxito y error 500) */
import { test, expect, Page } from '@playwright/test';
// ───────────────────────────────────────────── // Helpers / shared data // ─────────────────────────────────────────────
const USER = { email: 'ana.garcia@leadspark.io', password: 'Cultiva2025!', };
const NEW_LEAD = { name: 'Carlos Martínez', company: 'Inversiones Solaris S.L.', email: 'cmartinez@solaris.es', phone: '+34 612 345 678', value: '12000', };
// ───────────────────────────────────────────── // SUITE 1 — Autenticación // ─────────────────────────────────────────────
test.describe('Autenticación', () => { test('debería iniciar sesión con credenciales válidas', async ({ page }) => { await page.goto('/login');
await page.getByLabel('Email').fill(USER.email);
await page.getByLabel('Contraseña').fill(USER.password);
await page.getByRole('button', { name: 'Iniciar sesión' }).click();
await expect(page).toHaveURL('/dashboard');
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
await expect(page.getByText(USER.email)).toBeVisible();
});
test('debería mostrar error con credenciales incorrectas', async ({ page }) => { await page.goto('/login');
await page.getByLabel('Email').fill('falso@leadspark.io');
await page.getByLabel('Contraseña').fill('wrongpassword');
await page.getByRole('button', { name: 'Iniciar sesión' }).click();
await expect(page.getByRole('alert')).toHaveText(/credenciales incorrectas/i);
await expect(page).toHaveURL('/login');
});
test('debería mantener sesión tras recargar la página', async ({ page }) => { await page.goto('/login'); await page.getByLabel('Email').fill(USER.email); await page.getByLabel('Contraseña').fill(USER.password); await page.getByRole('button', { name: 'Iniciar sesión' }).click(); await expect(page).toHaveURL('/dashboard');
await page.reload();
await expect(page).toHaveURL('/dashboard');
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
});
test('debería cerrar sesión y redirigir a /login', async ({ page }) => { await page.goto('/dashboard');
await page.getByRole('button', { name: 'Perfil' }).click();
await page.getByRole('menuitem', { name: 'Cerrar sesión' }).click();
await expect(page).toHaveURL('/login');
await expect(page.getByRole('heading', { name: 'Inicia sesión' })).toBeVisible();
});
test('debería redirigir rutas protegidas a /login cuando no hay sesión', async ({ page }) => { await page.goto('/leads'); await expect(page).toHaveURL('/login'); }); });
// ───────────────────────────────────────────── // SUITE 2 — Gestión de Leads (CRUD) // ─────────────────────────────────────────────
test.describe('Gestión de Leads', () => { test.use({ storageState: 'tests/e2e/.auth/ana.json' });
test('debería crear un nuevo lead correctamente', async ({ page }) => { await page.goto('/leads/new');
await page.getByLabel('Nombre completo').fill(NEW_LEAD.name);
await page.getByLabel('Empresa').fill(NEW_LEAD.company);
await page.getByLabel('Email').fill(NEW_LEAD.email);
await page.getByLabel('Teléfono').fill(NEW_LEAD.phone);
await page.getByLabel('Valor estimado (€)').fill(NEW_LEAD.value);
await page.getByRole('combobox', { name: 'Etapa' }).selectOption('Prospecto');
await page.getByRole('button', { name: 'Guardar lead' }).click();
await expect(page.getByRole('alert')).toHaveText(/lead creado/i);
await expect(page).toHaveURL(/\/leads\/\d+/);
await expect(page.getByRole('heading', { name: NEW_LEAD.name })).toBeVisible();
});
test('debería mostrar errores de validación en campos requeridos', async ({ page }) => { await page.goto('/leads/new');
await page.getByRole('button', { name: 'Guardar lead' }).click();
await expect(page.getByText('El nombre es obligatorio')).toBeVisible();
await expect(page.getByText('El email es obligatorio')).toBeVisible();
});
test('debería validar formato de email incorrecto', async ({ page }) => { await page.goto('/leads/new');
await page.getByLabel('Nombre completo').fill('Test Lead');
await page.getByLabel('Email').fill('no-es-un-email');
await page.getByRole('button', { name: 'Guardar lead' }).click();
await expect(page.getByText('Email no válido')).toBeVisible();
});
test('debería editar un lead existente', async ({ page }) => { await page.goto('/leads');
await page.getByRole('row', { name: /Inversiones Solaris/ })
.getByRole('button', { name: 'Editar' })
.click();
await page.getByLabel('Empresa').clear();
await page.getByLabel('Empresa').fill('Solaris Global S.A.');
await page.getByRole('button', { name: 'Guardar cambios' }).click();
await expect(page.getByRole('alert')).toHaveText(/cambios guardados/i);
await expect(page.getByText('Solaris Global S.A.')).toBeVisible();
});
test('debería eliminar un lead con confirmación de diálogo', async ({ page }) => { await page.goto('/leads');
await page.getByRole('row', { name: /Solaris Global/ })
.getByRole('button', { name: 'Eliminar' })
.click();
// Diálogo de confirmación
await expect(page.getByRole('dialog')).toBeVisible();
await expect(page.getByRole('dialog')).toHaveText(/¿Confirmas la eliminación/i);
await page.getByRole('button', { name: 'Sí, eliminar' }).click();
await expect(page.getByRole('alert')).toHaveText(/lead eliminado/i);
await expect(page.getByText('Solaris Global')).not.toBeVisible();
});
test('debería buscar leads por nombre', async ({ page }) => { await page.goto('/leads');
await page.getByPlaceholder('Buscar leads…').fill('Martínez');
await page.getByRole('button', { name: 'Buscar' }).click();
const rows = page.getByRole('row').filter({ hasText: 'Martínez' });
await expect(rows).not.toHaveCount(0);
await expect(page.getByText('García López')).not.toBeVisible();
});
test('debería mostrar estado vacío cuando la búsqueda no da resultados', async ({ page }) => { await page.goto('/leads');
await page.getByPlaceholder('Buscar leads…').fill('xyzimpossiblequery');
await page.getByRole('button', { name: 'Buscar' }).click();
await expect(page.getByText('No se encontraron leads')).toBeVisible();
}); });
// ───────────────────────────────────────────── // SUITE 3 — Pipeline Kanban // ─────────────────────────────────────────────
test.describe('Pipeline Kanban', () => { test.use({ storageState: 'tests/e2e/.auth/ana.json' });
test('debería mostrar las cuatro columnas del pipeline', async ({ page }) => { await page.goto('/pipeline');
for (const col of ['Prospecto', 'Contactado', 'Propuesta', 'Cerrado']) {
await expect(page.getByRole('region', { name: col })).toBeVisible();
}
});
test('debería mover un lead a la siguiente etapa con drag & drop', async ({ page }) => { await page.goto('/pipeline');
const sourceCard = page.locator('[data-testid="kanban-card"]', { hasText: 'Carlos Martínez' });
const targetColumn = page.getByRole('region', { name: 'Contactado' });
await sourceCard.dragTo(targetColumn);
await expect(targetColumn.locator('[data-testid="kanban-card"]', { hasText: 'Carlos Martínez' })).toBeVisible();
});
test('debería filtrar el pipeline por propietario', async ({ page }) => { await page.goto('/pipeline');
await page.getByRole('combobox', { name: 'Propietario' }).selectOption('Ana García');
const allCards = page.locator('[data-testid="kanban-card"]');
const count = await allCards.count();
for (let i = 0; i < count; i++) {
await expect(allCards.nth(i).getByText('Ana García')).toBeVisible();
}
});
test('debería mostrar el panel de detalle al hacer clic en una tarjeta', async ({ page }) => { await page.goto('/pipeline');
await page.locator('[data-testid="kanban-card"]', { hasText: 'Carlos Martínez' }).click();
await expect(page.getByRole('complementary', { name: 'Detalle del lead' })).toBeVisible();
await expect(page.getByText(NEW_LEAD.email)).toBeVisible();
await expect(page.getByText('12.000 €')).toBeVisible();
}); });
// ───────────────────────────────────────────── // SUITE 4 — Dashboard con API Mocking // ─────────────────────────────────────────────
test.describe('Dashboard — métricas con API mocked', () => { test.use({ storageState: 'tests/e2e/.auth/ana.json' });
test('debería mostrar métricas de conversión desde la API', async ({ page }) => { await page.route('**/api/metrics', (route) => { route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ totalLeads: 142, leadsConvertidos: 38, tasaConversion: 26.8, ingresosMes: 87500, leadsPorEtapa: { Prospecto: 62, Contactado: 41, Propuesta: 21, Cerrado: 18, }, }), }); });
await page.goto('/dashboard');
await expect(page.getByTestId('metric-total-leads')).toHaveText('142');
await expect(page.getByTestId('metric-conversion-rate')).toHaveText('26,8 %');
await expect(page.getByTestId('metric-revenue')).toHaveText('87.500 €');
await expect(page.getByRole('img', { name: 'Gráfico de pipeline' })).toBeVisible();
});
test('debería manejar error 500 de la API con mensaje amigable', async ({ page }) => { await page.route('**/api/metrics', (route) => { route.fulfill({ status: 500 }); });
await page.goto('/dashboard');
await expect(page.getByRole('alert')).toHaveText(/no se pudieron cargar las métricas/i);
await expect(page.getByRole('button', { name: 'Reintentar' })).toBeVisible();
});
test('debería reintentar la carga de métricas al hacer clic en Reintentar', async ({ page }) => { let callCount = 0;
await page.route('**/api/metrics', (route) => {
callCount++;
if (callCount === 1) {
route.fulfill({ status: 500 });
} else {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
totalLeads: 58,
leadsConvertidos: 14,
tasaConversion: 24.1,
ingresosMes: 32000,
}),
});
}
});
await page.goto('/dashboard');
await expect(page.getByRole('alert')).toHaveText(/no se pudieron cargar/i);
await page.getByRole('button', { name: 'Reintentar' }).click();
await expect(page.getByTestId('metric-total-leads')).toHaveText('58');
await expect(page.getByRole('alert')).not.toBeVisible();
}); });
// ───────────────────────────────────────────── // SUITE 5 — Navegación y responsive // ─────────────────────────────────────────────
test.describe('Navegación y responsive', () => { test.use({ storageState: 'tests/e2e/.auth/ana.json' });
test('debería navegar entre secciones principales usando el menú lateral', async ({ page }) => { await page.goto('/dashboard');
await page.getByRole('link', { name: 'Leads' }).click();
await expect(page).toHaveURL('/leads');
await expect(page.getByRole('heading', { level: 1 })).toHaveText('Leads');
await page.getByRole('link', { name: 'Pipeline' }).click();
await expect(page).toHaveURL('/pipeline');
});
test('debería mostrar menú hamburguesa en móvil', async ({ page }) => { await page.setViewportSize({ width: 390, height: 844 }); // iPhone 14 await page.goto('/dashboard');
await expect(page.getByRole('navigation')).not.toBeVisible();
await page.getByRole('button', { name: 'Abrir menú' }).click();
await expect(page.getByRole('navigation')).toBeVisible();
});
test('debería cerrar el menú móvil al seleccionar una opción', async ({ page }) => { await page.setViewportSize({ width: 390, height: 844 }); await page.goto('/dashboard');
await page.getByRole('button', { name: 'Abrir menú' }).click();
await page.getByRole('link', { name: 'Leads' }).click();
await expect(page.getByRole('navigation')).not.toBeVisible();
await expect(page).toHaveURL('/leads');
}); });
// qué_hace
Genera tests E2E listos para producción con Playwright a partir de cualquier descripción de comportamiento o componente.
// cómo_lo_hace
Explora la configuración y convenciones del proyecto, selecciona plantillas de patrones (auth, CRUD, formularios, búsqueda) y produce archivos .spec.ts/.spec.js con locators semánticos, aserciones web-first y cobertura de casos límite.
// ejemplo_de_uso
Para equipos que quieren automatizar pruebas E2E sin escribir tests desde cero. Ej.: describir el flujo de registro de usuario y recibir un archivo .spec.ts con Playwright que cubre el happy path y los casos de error principales.
// plataformas
// opiniones_de_la_comunidad
Opiniones
Cargando opiniones…
// pase_cultiva_ia
Llévate todo el arsenal con el Pase
Todas las skills, prompts y automatizaciones del catálogo en un único archivo, listas para usar: un pago, acceso de por vida y las novedades que añadamos. Sin suscripción.
Pago único · IVA incluido · pago seguro con Stripe.
Acceso inmediato · si no es lo que esperabas, te devolvemos los 10 €.