¿Qué es Lighthouse?

Lighthouse es una herramienta automatizada de código abierto de Google para mejorar la calidad de páginas web. Proporciona auditorías integrales de rendimiento, accesibilidad, progressive web apps, SEO y mejores prácticas. Para ingenieros QA, Lighthouse es esencial para validar que las aplicaciones cumplan estándares modernos de rendimiento web, especialmente Core Web Vitals — las métricas clave de Google para experiencia de usuario.

Por qué Lighthouse Importa para QA

  • Core Web Vitals Impactan SEO - Google usa LCP, FID y CLS como factores de ranking
  • Validación de Experiencia de Usuario - Cuantifica el rendimiento percibido con métricas reales
  • Integración CI/CD - Automatiza pruebas de regresión de rendimiento en pipelines
  • Insights Accionables - Obtén recomendaciones específicas para mejoras de rendimiento
  • Rendimiento Móvil - Prueba diseño responsive y problemas específicos de móvil

Core Web Vitals Explicados

Core Web Vitals son tres métricas clave que miden la experiencia de usuario en el mundo real:

1. Largest Contentful Paint (LCP)

Qué mide: Rendimiento de carga - cuando el elemento de contenido más grande se vuelve visible.

Umbrales:

  • Bueno: ≤ 2.5 segundos
  • Necesita Mejora: 2.5 - 4.0 segundos
  • Pobre: > 4.0 segundos

Problemas Comunes:

// Problema: JavaScript bloqueante de renderizado
<script src="analytics.js"></script>

// Solución: Diferir scripts no críticos
<script src="analytics.js" defer></script>

// Problema: Imágenes no optimizadas
<img src="hero-10mb.jpg" alt="Hero">

// Solución: Formatos modernos + imágenes responsive
<picture>
  <source srcset="hero.avif" type="image/avif">
  <source srcset="hero.webp" type="image/webp">
  <img src="hero.jpg" alt="Hero" loading="lazy">
</picture>

Estrategias de Optimización:

  • Usar CDN para assets estáticos
  • Implementar lazy loading para imágenes below-fold
  • Precargar recursos críticos: <link rel="preload" href="hero.jpg" as="image">
  • Optimizar tiempos de respuesta del servidor (TTFB < 600ms)
  • Usar resource hints: dns-prefetch, preconnect

2. First Input Delay (FID) / Interaction to Next Paint (INP)

Qué mide: Interactividad - retraso entre interacción del usuario y respuesta del navegador.

Umbrales FID (siendo reemplazado por INP):

  • Bueno: ≤ 100ms
  • Necesita Mejora: 100 - 300ms
  • Pobre: > 300ms

Umbrales INP (nueva métrica desde 2024):

  • Bueno: ≤ 200ms
  • Necesita Mejora: 200 - 500ms
  • Pobre: > 500ms

Problemas Comunes:

// Problema: Tareas largas de JavaScript bloquean el hilo principal
function processHugeDataset() {
  // Loop síncrono procesando 100k items
  for (let i = 0; i < 100000; i++) {
    heavyCalculation(data[i]);
  }
}

// Solución: Dividir en chunks pequeños con setTimeout
async function processHugeDataset() {
  const chunkSize = 1000;
  for (let i = 0; i < data.length; i += chunkSize) {
    const chunk = data.slice(i, i + chunkSize);
    chunk.forEach(item => heavyCalculation(item));

    // Ceder al navegador cada chunk
    await new Promise(resolve => setTimeout(resolve, 0));
  }
}

// Solución: Usar Web Workers para cálculos pesados
// main.js
const worker = new Worker('calculator.worker.js');
worker.postMessage({ data: largeDataset });
worker.onmessage = (e) => {
  displayResults(e.data);
};

// calculator.worker.js
self.onmessage = (e) => {
  const results = heavyCalculation(e.data);
  self.postMessage(results);
};

Estrategias de Optimización:

  • Dividir tareas largas en chunks pequeños (< 50ms cada uno)
  • Usar code splitting para reducir tamaño de bundle JavaScript
  • Diferir scripts de terceros
  • Implementar service workers para mejor caching
  • Usar requestIdleCallback para trabajo no urgente

3. Cumulative Layout Shift (CLS)

Qué mide: Estabilidad visual - cambios inesperados de layout durante carga de página.

Umbrales:

  • Bueno: ≤ 0.1
  • Necesita Mejora: 0.1 - 0.25
  • Pobre: > 0.25

Problemas Comunes:

<!-- Problema: Imágenes sin dimensiones -->
<img src="product.jpg" alt="Product">

<!-- Solución: Siempre especificar width/height -->
<img src="product.jpg" alt="Product" width="800" height="600">

<!-- Problema: Inserción dinámica de contenido -->
<div id="banner"></div>
<script>
  // Carga ad asíncronamente, empujando contenido hacia abajo
  loadAdvertisement('#banner');
</script>

<!-- Solución: Reservar espacio para contenido dinámico -->
<div id="banner" style="min-height: 250px;">
  <!-- Ad se carga aquí sin layout shift -->
</div>
/* Problema: Web fonts causan layout shift (FOIT/FOUT) */
@font-face {
  font-family: 'CustomFont';
  src: url('custom-font.woff2') format('woff2');
}

/* Solución: Usar font-display para controlar comportamiento de carga */
@font-face {
  font-family: 'CustomFont';
  src: url('custom-font.woff2') format('woff2');
  font-display: swap; /* Mostrar fallback inmediatamente, intercambiar cuando cargue */
}

/* Mejor: Precargar fuentes críticas */
/* En HTML: <link rel="preload" href="custom-font.woff2" as="font" crossorigin> */

Estrategias de Optimización:

  • Siempre incluir atributos de tamaño en imágenes y videos
  • Reservar espacio para ads y embeds con min-height
  • Evitar insertar contenido arriba de contenido existente
  • Usar animaciones transform en lugar de propiedades que disparan layout
  • Precargar fuentes personalizadas y usar font-display: swap

Ejecutar Pruebas Lighthouse

1. Chrome DevTools (Pruebas Manuales)

# Abrir Chrome DevTools
# 1. Presionar F12 o Cmd+Option+I
# 2. Ir a pestaña "Lighthouse"
# 3. Seleccionar categorías (Performance, Accessibility, etc.)
# 4. Elegir dispositivo (Mobile/Desktop)
# 5. Hacer clic en "Analyze page load"

Mejores Prácticas para Pruebas Manuales:

  • Probar en modo Incógnito (sin interferencia de extensiones)
  • Usar throttling para simular condiciones del mundo real (Slow 4G, 4x CPU slowdown)
  • Ejecutar múltiples pruebas y promediar puntuaciones (resultados pueden variar ±5 puntos)
  • Probar configuraciones móvil y desktop

2. Lighthouse CLI (Pruebas Automatizadas)

# Instalar Lighthouse
npm install -g lighthouse

# Prueba básica
lighthouse https://example.com

# Prueba móvil con formatos de salida
lighthouse https://example.com \
  --output html,json,csv \
  --output-path ./reports/report \
  --view \
  --chrome-flags="--headless"

# Prueba con throttling específico
lighthouse https://example.com \
  --throttling.rttMs=150 \
  --throttling.throughputKbps=1600 \
  --throttling.cpuSlowdownMultiplier=4

# Probar solo categorías específicas
lighthouse https://example.com \
  --only-categories=performance,accessibility

# Archivo de config personalizado
lighthouse https://example.com \
  --config-path=./lighthouse-config.js

3. Lighthouse CI (Integración Continua)

# .github/workflows/lighthouse.yml
name: Lighthouse CI

on:
  pull_request:
    branches: [main]

jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'

      - name: Install dependencies
        run: npm ci

      - name: Build application
        run: npm run build

      - name: Start server
        run: npm start &

      - name: Wait for server
        run: npx wait-on http://localhost:3000

      - name: Run Lighthouse CI
        run: |
          npm install -g @lhci/cli
          lhci autorun
        env:
          LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}

      - name: Upload results
        uses: actions/upload-artifact@v3
        with:
          name: lighthouse-reports
          path: .lighthouseci/

Configuración lighthouserc.json:

{
  "ci": {
    "collect": {
      "url": ["http://localhost:3000"],
      "numberOfRuns": 3,
      "settings": {
        "preset": "desktop",
        "throttling": {
          "rttMs": 40,
          "throughputKbps": 10240,
          "cpuSlowdownMultiplier": 1
        }
      }
    },
    "assert": {
      "preset": "lighthouse:recommended",
      "assertions": {
        "categories:performance": ["error", {"minScore": 0.9}],
        "categories:accessibility": ["error", {"minScore": 0.9}],
        "first-contentful-paint": ["error", {"maxNumericValue": 2000}],
        "largest-contentful-paint": ["error", {"maxNumericValue": 2500}],
        "cumulative-layout-shift": ["error", {"maxNumericValue": 0.1}],
        "total-blocking-time": ["error", {"maxNumericValue": 300}]
      }
    },
    "upload": {
      "target": "temporary-public-storage"
    }
  }
}

Interpretar Puntuaciones Lighthouse

Desglose de Puntuación de Rendimiento

La puntuación de Performance (0-100) se pondera entre múltiples métricas:

MétricaPesoDescripción
First Contentful Paint (FCP)10%Primer texto/imagen renderizado
Largest Contentful Paint (LCP)25%Elemento de contenido más grande visible
Total Blocking Time (TBT)30%Tiempo total que el hilo principal está bloqueado
Cumulative Layout Shift (CLS)25%Puntuación de estabilidad visual
Speed Index10%Qué tan rápido se muestra visualmente el contenido

Rangos de Puntuación:

  • 90-100 (Verde): Bueno - cumple mejores prácticas de rendimiento
  • 50-89 (Naranja): Necesita mejora - algo de optimización requerida
  • 0-49 (Rojo): Pobre - problemas significativos de rendimiento

Estrategia de Presupuesto de Rendimiento

// lighthouserc-budget.json
{
  "ci": {
    "assert": {
      "assertions": {
        // Umbrales Core Web Vitals
        "largest-contentful-paint": ["error", {"maxNumericValue": 2500}],
        "cumulative-layout-shift": ["error", {"maxNumericValue": 0.1}],
        "total-blocking-time": ["error", {"maxNumericValue": 300}],

        // Presupuestos de recursos
        "resource-summary:script:size": ["error", {"maxNumericValue": 300000}],
        "resource-summary:image:size": ["error", {"maxNumericValue": 500000}],
        "resource-summary:stylesheet:size": ["error", {"maxNumericValue": 50000}],
        "resource-summary:document:size": ["error", {"maxNumericValue": 50000}],
        "resource-summary:font:size": ["error", {"maxNumericValue": 100000}],
        "resource-summary:third-party:size": ["error", {"maxNumericValue": 200000}],

        // Presupuestos de cantidad de solicitudes
        "resource-summary:script:count": ["error", {"maxNumericValue": 15}],
        "resource-summary:third-party:count": ["error", {"maxNumericValue": 10}]
      }
    }
  }
}

Técnicas Avanzadas de Lighthouse

1. Auditorías Personalizadas

// custom-audit.js
class CustomPerformanceAudit {
  static get meta() {
    return {
      id: 'custom-api-performance',
      title: 'Tiempo de Respuesta API',
      failureTitle: 'Respuestas API son lentas',
      description: 'Mide tiempos de respuesta de endpoints API',
      requiredArtifacts: ['devtoolsLogs']
    };
  }

  static async audit(artifacts) {
    const devtools = artifacts.devtoolsLogs.defaultPass;
    const networkRecords = /* extraer registros de red */;

    const apiCalls = networkRecords.filter(r =>
      r.url.includes('/api/') && r.finished
    );

    const avgResponseTime = apiCalls.reduce((sum, call) =>
      sum + call.endTime - call.startTime, 0
    ) / apiCalls.length;

    const passed = avgResponseTime < 500; // umbral 500ms

    return {
      score: passed ? 1 : 0,
      numericValue: avgResponseTime,
      displayValue: `${Math.round(avgResponseTime)}ms promedio`,
      details: {
        type: 'table',
        headings: [
          {key: 'url', text: 'URL'},
          {key: 'responseTime', text: 'Tiempo Respuesta'}
        ],
        items: apiCalls.map(call => ({
          url: call.url,
          responseTime: `${Math.round(call.endTime - call.startTime)}ms`
        }))
      }
    };
  }
}

module.exports = CustomPerformanceAudit;

2. Pruebas de Flujo de Usuario (Nuevo en Lighthouse 9+)

// user-flow-test.js
import {startFlow} from 'lighthouse/lighthouse-core/fraggle-rock/api.js';
import puppeteer from 'puppeteer';

async function runUserFlow() {
  const browser = await puppeteer.launch({headless: 'new'});
  const page = await browser.newPage();

  const flow = await startFlow(page, {name: 'Flujo checkout e-commerce'});

  // Paso 1: Navegar a homepage
  await flow.navigate('https://example.com', {
    stepName: 'Homepage'
  });

  // Paso 2: Buscar producto
  await flow.startTimespan({stepName: 'Interacción búsqueda'});
  await page.type('#search', 'laptop');
  await page.click('#search-button');
  await page.waitForSelector('.search-results');
  await flow.endTimespan();

  // Paso 3: Página detalles producto
  await flow.navigate('https://example.com/product/123', {
    stepName: 'Página producto'
  });

  // Paso 4: Interacción añadir al carrito
  await flow.startTimespan({stepName: 'Añadir al carrito'});
  await page.click('#add-to-cart');
  await page.waitForSelector('.cart-notification');
  await flow.endTimespan();

  // Paso 5: Snapshot checkout
  await flow.snapshot({stepName: 'Estado página carrito'});

  // Generar reporte
  const report = await flow.generateReport();
  fs.writeFileSync('user-flow-report.html', report);

  await browser.close();
}

runUserFlow();

3. Dashboard de Monitoreo de Rendimiento

# lighthouse-monitor.py
import subprocess
import json
import datetime
from influxdb import InfluxDBClient

def run_lighthouse(url):
    """Ejecutar Lighthouse y retornar métricas"""
    result = subprocess.run([
        'lighthouse',
        url,
        '--output=json',
        '--output-path=stdout',
        '--chrome-flags="--headless"'
    ], capture_output=True, text=True)

    return json.loads(result.stdout)

def extract_metrics(report):
    """Extraer métricas clave del reporte Lighthouse"""
    audits = report['audits']

    return {
        'performance_score': report['categories']['performance']['score'] * 100,
        'fcp': audits['first-contentful-paint']['numericValue'],
        'lcp': audits['largest-contentful-paint']['numericValue'],
        'tbt': audits['total-blocking-time']['numericValue'],
        'cls': audits['cumulative-layout-shift']['numericValue'],
        'speed_index': audits['speed-index']['numericValue'],
    }

def send_to_influxdb(metrics, url):
    """Enviar métricas a InfluxDB para visualización Grafana"""
    client = InfluxDBClient(host='localhost', port=8086, database='lighthouse')

    point = {
        'measurement': 'performance',
        'tags': {
            'url': url,
            'device': 'mobile'
        },
        'time': datetime.datetime.utcnow().isoformat(),
        'fields': metrics
    }

    client.write_points([point])

# Monitorear páginas cada hora
urls = [
    'https://example.com',
    'https://example.com/products',
    'https://example.com/checkout'
]

for url in urls:
    report = run_lighthouse(url)
    metrics = extract_metrics(report)
    send_to_influxdb(metrics, url)
    print(f"✓ {url}: Puntuación Rendimiento {metrics['performance_score']}")

Cuellos de Botella Comunes y Correcciones

Problema: Bundles JavaScript Grandes

# Analizar tamaño de bundle
npm install -g webpack-bundle-analyzer

# Añadir a webpack.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
};

# Build y analizar
npm run build

Soluciones:

  • Code splitting: import('./heavy-component.js').then(module => ...)
  • Tree shaking: Asegurar "sideEffects": false en package.json
  • Eliminar dependencias no usadas
  • Usar imports dinámicos para code splitting basado en rutas

Problema: Recursos Bloqueantes de Renderizado

<!-- Problema: CSS bloqueante -->
<link rel="stylesheet" href="styles.css">

<!-- Solución: CSS crítico inline, diferir no crítico -->
<style>
  /* CSS crítico above-fold inline */
  .header { /* ... */ }
  .hero { /* ... */ }
</style>
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>

Problema: Imágenes No Optimizadas

# Instalar herramientas de optimización de imágenes
npm install -D imagemin imagemin-webp imagemin-avif

# optimize-images.js
const imagemin = require('imagemin');
const imageminWebp = require('imagemin-webp');
const imageminAvif = require('imagemin-avif');

(async () => {
  await imagemin(['images/*.{jpg,png}'], {
    destination: 'images/optimized',
    plugins: [
      imageminWebp({quality: 80}),
      imageminAvif({quality: 65})
    ]
  });
})();

Conclusión

Lighthouse es una herramienta indispensable para pruebas modernas de rendimiento web. Al integrar Lighthouse en tu flujo de trabajo QA — desde auditorías manuales DevTools hasta pipelines CI/CD (como se discute en Load Testing with JMeter: Complete Guide) automatizados — aseguras que las aplicaciones cumplan estándares Core Web Vitals y entreguen experiencias excepcionales de usuario.

Conclusiones Clave:

  • Enfócate en Core Web Vitals: LCP, INP/FID, CLS
  • Automatiza pruebas Lighthouse en pipelines CI/CD
  • (como se discute en Burp Suite for QA Engineers: Complete Security Testing Guide) Establece presupuestos de rendimiento y aplícalos
  • Usa pruebas User Flow para interacciones complejas
  • Monitorea rendimiento en el tiempo con dashboards
  • Optimiza imágenes, JavaScript y rutas de renderizado

El rendimiento es una característica, no una reflexión tardía. Haz de las pruebas Lighthouse una parte central de tu proceso QA.