125 lines
3.9 KiB
Markdown
125 lines
3.9 KiB
Markdown
# ABE — Visual Regression Testing Specification
|
|
|
|
## Concepto
|
|
ABE toma screenshots durante la exploración. En vez de solo guardarlos,
|
|
los compara contra una baseline aprobada para detectar cambios visuales
|
|
inesperados entre ejecuciones. Inspirado en Percy y Chromatic,
|
|
pero integrado directamente en el flujo de exploración autónoma.
|
|
|
|
## Cómo funciona
|
|
|
|
### Primera ejecución (sin baseline)
|
|
1. ABE explora el app, toma screenshots de cada estado descubierto
|
|
2. Todos los screenshots se marcan como "pending review" en la UI
|
|
3. El usuario aprueba o rechaza cada uno desde la GUI
|
|
4. Los aprobados se convierten en la BASELINE
|
|
|
|
### Ejecuciones posteriores
|
|
1. ABE explora el app, toma screenshots de cada estado
|
|
2. Para cada screenshot, busca la baseline correspondiente por state_id (hash DOM+URL)
|
|
3. Si no hay baseline: marcar como "new state", notificar
|
|
4. Si hay baseline: comparar usando pixelmatch (npm library)
|
|
5. Si diff > threshold (default 0.1%): crear anomalía tipo visual_regression
|
|
6. Si diff <= threshold: marcar como "passed"
|
|
|
|
## Librería de comparación
|
|
|
|
Usar `pixelmatch` (npm) para comparación pixel a pixel.
|
|
Usar `sharp` para resize y normalización de imágenes antes de comparar.
|
|
```typescript
|
|
import pixelmatch from 'pixelmatch';
|
|
import sharp from 'sharp';
|
|
|
|
async function compareScreenshots(
|
|
baselinePath: string,
|
|
currentPath: string,
|
|
diffOutputPath: string,
|
|
threshold: number = 0.1
|
|
): Promise<{ diffPixels: number; diffPercent: number; hasDiff: boolean }> {
|
|
// resize both to same dimensions, compare, generate diff image
|
|
}
|
|
```
|
|
|
|
## Modelo de datos — añadir a SQLite
|
|
|
|
### Table: visual_baselines
|
|
```sql
|
|
CREATE TABLE IF NOT EXISTS visual_baselines (
|
|
id TEXT PRIMARY KEY,
|
|
state_id TEXT NOT NULL,
|
|
url TEXT NOT NULL,
|
|
screenshot_path TEXT NOT NULL,
|
|
approved_at INTEGER NOT NULL,
|
|
approved_by TEXT DEFAULT 'user',
|
|
width INTEGER NOT NULL,
|
|
height INTEGER NOT NULL
|
|
);
|
|
```
|
|
|
|
### Table: visual_comparisons
|
|
```sql
|
|
CREATE TABLE IF NOT EXISTS visual_comparisons (
|
|
id TEXT PRIMARY KEY,
|
|
session_id TEXT NOT NULL,
|
|
state_id TEXT NOT NULL,
|
|
baseline_id TEXT,
|
|
current_screenshot_path TEXT NOT NULL,
|
|
diff_screenshot_path TEXT,
|
|
diff_pixels INTEGER,
|
|
diff_percent REAL,
|
|
status TEXT NOT NULL, -- 'passed' | 'failed' | 'new_state' | 'pending'
|
|
created_at INTEGER NOT NULL
|
|
);
|
|
```
|
|
|
|
## Nuevo tipo de anomalía
|
|
|
|
Añadir a AnomalyDetector:
|
|
- type: `visual_regression`
|
|
- severity: calculado por diff_percent:
|
|
- < 1% → low
|
|
- 1-5% → medium
|
|
- 5-15% → high
|
|
- > 15% → critical
|
|
- description: "Visual regression detected: X% of pixels changed"
|
|
- evidence: baseline screenshot + current screenshot + diff image (highlighted in red)
|
|
|
|
## Nuevo endpoint de API
|
|
|
|
### GET /api/visual/comparisons
|
|
Lista todas las comparaciones de la sesión más reciente.
|
|
Query: ?status=failed&sessionId=xxx
|
|
|
|
### POST /api/visual/baselines/:comparisonId/approve
|
|
Aprueba un screenshot como nueva baseline.
|
|
|
|
### POST /api/visual/baselines/:comparisonId/reject
|
|
Rechaza (anomalía confirmada, no actualizar baseline).
|
|
|
|
### POST /api/visual/baselines/approve-all
|
|
Aprueba todos los "new_state" pendientes de una sesión.
|
|
|
|
## Frontend — nueva sección Visual Review
|
|
|
|
Nueva página /visual-review:
|
|
- Grid de cards, cada una muestra: URL del estado, thumbnail del screenshot actual
|
|
- Filtros: passed | failed | new_state | pending
|
|
- Click en una card abre modal con:
|
|
- Vista lado a lado: baseline izquierda, actual derecha
|
|
- Vista diff: imagen con píxeles cambiados en rojo
|
|
- Porcentaje de cambio
|
|
- Botones: Approve as new baseline | Mark as bug | Ignore
|
|
- Bulk actions: "Approve all new states", "Mark all failed as bugs"
|
|
|
|
## Configuración
|
|
|
|
Añadir a ExplorationConfig:
|
|
```typescript
|
|
visualRegression: {
|
|
enabled: boolean; // default: true
|
|
threshold: number; // default: 0.001 (0.1%)
|
|
screenshotFullPage: boolean; // default: false (solo viewport)
|
|
ignoreSelectors: string[]; // e.g. [".timestamp", ".ad-banner"] — excluir zonas dinámicas
|
|
}
|
|
```
|