Resumen Ejecutivo
4.2
Score medio / 10
24
Críticos
11
Advertencias
4
Archivos auditados
⚠️
La suite tiene calidad insuficiente para producción. El patrón dominante es
waitForTimeout() (12 ocurrencias) seguido de selectores CSS frágiles (8 ocurrencias) y URLs hardcodeadas (7 ocurrencias). Estos tres problemas explican los ~18 min de CI y las pruebas intermitentes. Tiempo estimado de corrección: 3–4 horas aplicando los fixes listados.
Puntuación por archivo
| Archivo | Score | Veredicto | Issues |
|---|---|---|---|
| auth.spec.ts | 2 | Necesita reescritura |
9 críticos
2 advertencias
|
| dashboard.spec.ts | 3 | Problemas significativos |
8 críticos
3 advertencias
2 sugerencias
|
| parcelas.spec.ts | 4 | Anti-patrones presentes |
7 críticos
4 advertencias
2 sugerencias
|
| fitosanitarios.spec.ts | 7 | Bueno, mejoras menores |
2 advertencias
3 sugerencias
|
Revisión detallada por archivo
2
auth.spec.ts
Login · Registro · Recuperación de contraseña
9 críticos
2 advertencias
🚨 Críticos
-
Líneas 12, 22, 27, 32 — Anti-patrón #1
waitForTimeout()usado 4 veces · Causa: tests lentos y flaky en CIReemplazar porawait expect(locator).toBeVisible()otoHaveURL() -
Líneas 7, 20 — Anti-patrón #6Variable
authTokencompartida entre tests · Los tests no son independientesMover setup de usuario atest.beforeEacho fixture compartido -
Líneas 9, 22, 28 — Anti-patrón #3URLs hardcodeadas:
'http://localhost:3000/login'· Rompen en entornos staging/prodUsar rutas relativas:page.goto('/login')(baseURL definida en playwright.config.ts) -
Líneas 10–11, 18–19 — Anti-patrón #4Selectores CSS/ID frágiles:
#email-input,.btn-primary· Rompen con refactors de CSSpage.getByLabel('Email').fill(...)·page.getByRole('button', { name: 'Entrar' }) -
Líneas 13, 23 — Anti-patrón #2
expect(await page.textContent(...))— aserción no-web-first · Comprueba una vez, no reintentaawait expect(page.getByText('Credenciales incorrectas')).toBeVisible() -
Línea 14 — Anti-patrón #11
page.evaluate()para leerlocalStorage· Código frágil y acoplado a implementaciónUsar fixture de autenticación de Playwright constorageState -
Línea 34 — Anti-patrón #5
page.click('.submit-btn')sinawait· Race condition garantizadaAñadirawait:await page.getByRole('button', { name: 'Guardar' }).click() -
Tests "step 1" y "step 2" — Anti-patrón #7Dependencia de orden de ejecución entre tests de recuperación de contraseñaConsolidar en un único test
'should complete password recovery flow' -
Tests líneas 6, 20 — Anti-patrón #13Nombres genéricos:
'login test','should fail with bad password'→'should redirect to dashboard after successful login'
⚠️ Advertencias
-
Global — Anti-patrón #10No hay test de cuenta bloqueada ni MFA requerido — casos de error críticos para auth
-
Global — Anti-patrón #9Credenciales de prueba dispersas como magic strings: extraer a constante
TEST_USER
3
dashboard.spec.ts
Vista principal · Métricas · Notificaciones
8 críticos
3 advertencias
2 sugerencias
🚨 Críticos
-
Líneas 8, 16, 21, 23, 27 — Anti-patrón #1
waitForTimeout()x5 — el test más lento de la suiteEsperar carga conawait expect(page.getByRole('main')).toBeVisible() -
Líneas 7, 14 — Anti-patrón #3URLs hardcodeadas:
'http://localhost:3000/dashboard'await page.goto('/dashboard') -
Líneas 9, 17–19 — Anti-patrón #11
page.evaluate()para contar elementos DOM y leer texto de métricasawait expect(page.getByTestId('metric-hectareas')).toContainText(/\d/) -
Líneas 7, 20, 26 — Anti-patrón #4Selectores CSS:
.welcome-card,.refresh-btn,.nav-link[href=...]page.getByRole('link', { name: 'Parcelas' }) -
Línea 31 — Anti-patrón #19
page.waitForNetworkIdle()— espera arbitraria, falsa sensación de estabilidadEsperar elemento concreto:await expect(page.getByRole('list', { name: 'notificaciones' })).toBeVisible() -
Líneas 5, 12 — Anti-patrón #13Nombres genéricos:
'should work','metrics panel test'→'should display welcome card after login'·'should show hectareas and alerts metrics'
⚠️ Advertencias
-
Global — Anti-patrón #14Dashboard tiene 10+ locators sin Page Object — mantenimiento costoso
-
Global — Anti-patrón #18No se verifican errores de consola — fallos silenciosos de JS no detectados
-
Global — Anti-patrón #20No hay
test.describe()agrupando los tests del dashboard
4
parcelas.spec.ts
CRUD de parcelas agrícolas
7 críticos
4 advertencias
2 sugerencias
🚨 Críticos
-
Líneas 16, 26, 31, 33, 36 — Anti-patrón #1
waitForTimeout()x5 — incluyendo waits encadenados en el flujo de borradoDespués de.click()del modal, esperarexpect(dialog).not.toBeVisible() -
Línea 24 — Anti-patrón #3URL hardcodeada con ID:
'http://localhost:3000/parcelas/123/editar'Crear parcela vía API enbeforeEachy usar su ID dinámico -
Líneas 25–26, 31–33 — Anti-patrón #4Selectores CSS:
input[name="nombre"],.save-button,.parcela-row:first-child .delete-btnUsargetByLabel,getByRole('button'),getByRole('row').first() -
Línea 35 — sin aserción post-deleteTest de borrado sin verificar que la parcela desapareció de la lista
await expect(page.getByRole('cell', { name: parcelaName })).not.toBeVisible() -
Líneas 3–6 — Anti-patrón #12Anidamiento triple de
test.describe()— dificulta encontrar el test en el outputMáximo 2 niveles:describe('Parcelas') > test('should create...')
⚠️ Advertencias
-
Test 'crear parcela' — Anti-patrón #10No hay test de validación: ¿qué pasa con hectáreas negativas o cultivo no permitido?
-
Global — Anti-patrón #15Datos de parcela inlined en cada test — extraer a factory
createParcelaData()
7
fitosanitarios.spec.ts
Gestión de productos fitosanitarios · Mejor archivo de la suite
2 advertencias
3 sugerencias
⚠️ Advertencias
-
Global — Anti-patrón #16No hay aserciones de accesibilidad — crítico para SaaS con posibles usuarios con discapacidadAñadir
await expect(page).toHaveNoViolations()con@axe-core/playwright -
Global — Anti-patrón #18Errores de consola no verificados — el formulario podría lanzar excepciones silenciosasAñadir en
beforeEach:page.on('console', msg => { if (msg.type() === 'error') errors.push(msg) })
💡 Sugerencias
-
Anti-patrón #17Añadir visual regression test para la tabla de productos:
await expect(page).toHaveScreenshot('fitosanitarios-table.png') -
Anti-patrón #10Añadir test de producto caducado: verificar que aparece badge de alerta rojo
-
Anti-patrón #15Constante
TEST_PRODUCTes buena práctica — extraerla a fixtures compartidos entre archivos
Plan de acción priorizado
35 fixes accionables| # | Prioridad | Anti-patrón | Ocurrencias | Impacto en CI | Esfuerzo |
|---|---|---|---|---|---|
| 1 | P1 — Crítico | Eliminar todos los waitForTimeout() |
12 ocurrencias | ↓ ~8 min CI | 1 h |
| 2 | P1 — Crítico | Reemplazar URLs hardcodeadas por rutas relativas | 7 ocurrencias | Desbloquea staging/prod | 30 min |
| 3 | P1 — Crítico | Migrar selectores CSS/ID a getByRole / getByLabel |
8 ocurrencias | Elimina flakiness por refactor | 45 min |
| 4 | P2 — Alto | Eliminar estado compartido entre tests (authToken) |
2 ocurrencias | Permite ejecución en paralelo | 30 min |
| 5 | P2 — Alto | Convertir aserciones no-web-first a expect(locator) |
6 ocurrencias | Elimina falsos negativos | 30 min |
| 6 | P2 — Alto | Refactorizar tests con dependencia de orden | 1 flujo (2 tests) | Seguro para ejecución paralela | 20 min |
| 7 | P3 — Medio | Crear Page Object para Dashboard (10+ locators) | 1 archivo | Mantenibilidad | 45 min |
| 8 | P3 — Medio | Añadir tests de casos de error en auth y parcelas | 4 tests nuevos | Cobertura de edge cases | 1 h |
Gaps de cobertura identificados
Permisos y roles
Sin tests de acceso como técnico vs propietario. ¿Puede un técnico borrar parcelas?
Responsive / móvil
Cero tests con
page.setViewportSize(). AgroFlow se usa frecuentemente en campo desde móvil.
API mocking
Sin
page.route() para simular errores de red en carga de parcelas o fitosanitarios.
Accesibilidad
No integrado
@axe-core/playwright. Solo fitosanitarios.spec.ts tiene comentario al respecto.
Informes de riego
Módulo sin ningún test E2E — cobertura cero para feature crítica.
Visual regression
Sin
toHaveScreenshot() en ningún archivo. Los cambios de UI pasan desapercibidos.