Saltar al contenido principal
Testing Automatizado: SEO, Performance y Accesibilidad

Testing Automatizado: SEO, Performance y Accesibilidad

2 de junio de 2024 7 min de lectura
Compartir:

El testing manual de sitios web es lento, propenso a errores y no escala. Te muestro cómo implementar una suite de testing automática que valida todo: desde SEO hasta performance.

El Problema del Testing Manual

La mayoría de desarrolladores validan sus sitios manualmente:

#  Proceso manual típico
1. Abrir Lighthouse
2. Revisar meta tags manualmente  
3. Validar Schema.org en herramientas online
4. Probar responsive design en DevTools
5. Verificar accesibilidad con axe
6. Repetir para cada página...

Problemas comunes:

  • Tiempo: Horas de testing manual
  • Inconsistencia: Diferentes criterios cada vez
  • Errores: Fácil pasar por alto problemas
  • Escalabilidad: No funciona con muchas páginas
  • Regresiones: Cambios rompen funcionalidad existente

La Solución: Testing Automatizado Completo

Mi sistema ejecuta 278+ tests automáticos que validan todo el sitio en minutos:

// Una sola línea ejecuta TODOS los tests
npm run test:all

//  Ejecuta automáticamente:
// - 30 tests de Meta Tags
// - 36 tests de Dark Mode  
// - 27 tests de Image Optimization
// - 33 tests de Reading Time
// - 22 tests de Social Share
// - 35 tests de AI Metadata
// - Tests de SEO en producción
// - Tests de performance
// - Validación de Schema.org

Arquitectura de Testing

1. Unit Tests: Features Modulares

// src/features/meta-tags/__tests__/meta-tags.test.ts
import { describe, it, expect } from 'vitest';
import { MetaTagGenerator } from '../engine/generator.ts';

describe('Meta Tags Feature', () => {
  const generator = new MetaTagGenerator();
  
  describe('Basic Meta Tags', () => {
    it('should generate title and description', () => {
      const tags = generator.generateBasicTags({
        title: 'Test Page',
        description: 'Test description'
      });
      
      expect(tags).toContainEqual({
        name: 'title',
        content: 'Test Page'
      });
      
      expect(tags).toContainEqual({
        name: 'description', 
        content: 'Test description'
      });
    });
    
    it('should validate title length', () => {
      const longTitle = 'A'.repeat(100);
      
      expect(() => {
        generator.generateBasicTags({
          title: longTitle,
          description: 'Valid description'
        });
      }).toThrow('Title too long');
    });
  });
  
  describe('Open Graph Tags', () => {
    it('should generate complete OG tags', () => {
      const tags = generator.generateOpenGraphTags({
        title: 'Test Page',
        description: 'Test description',
        image: '/test-image.jpg',
        url: '/test-page'
      });
      
      expect(tags).toContainEqual({
        property: 'og:title',
        content: 'Test Page'
      });
      
      expect(tags).toContainEqual({
        property: 'og:type',
        content: 'website'
      });
    });
  });
});

2. Integration Tests: Features Trabajando Juntas

// src/__tests__/integration/seo-integration.test.ts
import { describe, it, expect } from 'vitest';
import { generateMetaTags } from '../features/meta-tags';
import { generateSchema } from '../features/schema';

describe('SEO Integration Tests', () => {
  it('should generate consistent data across features', () => {
    const pageData = {
      title: 'Test Article',
      description: 'Test description',
      url: '/blog/test-article',
      type: 'article' as const,
      publishedDate: new Date('2024-06-02')
    };
    
    // Generar meta tags
    const metaTags = generateMetaTags(pageData);
    
    // Generar schema
    const schema = generateSchema(pageData);
    
    // Validar consistencia
    expect(metaTags.title).toBe(schema.headline);
    expect(metaTags.description).toBe(schema.description);
    expect(metaTags.canonicalUrl).toBe(schema.mainEntityOfPage['@id']);
  });
});

3. Production Tests: Validación en Vivo

// src/__tests__/seo/production.test.ts
import { describe, it, expect, beforeAll } from 'vitest';
import { JSDOM } from 'jsdom';

const PRODUCTION_URL = 'https://cappato.dev';

describe('SEO Production Tests', () => {
  let dom: JSDOM;
  let document: Document;
  
  beforeAll(async () => {
    const response = await fetch(PRODUCTION_URL);
    const html = await response.text();
    dom = new JSDOM(html);
    document = dom.window.document;
  });
  
  describe('Meta Tags Validation', () => {
    it('should have proper title tag', () => {
      const title = document.querySelector('title')?.textContent;
      
      expect(title).toBeTruthy();
      expect(title!.length).toBeGreaterThan(10);
      expect(title!.length).toBeLessThan(60);
    });
    
    it('should have meta description', () => {
      const description = document.querySelector('meta[name="description"]')?.getAttribute('content');
      
      expect(description).toBeTruthy();
      expect(description!.length).toBeGreaterThan(50);
      expect(description!.length).toBeLessThan(160);
    });
    
    it('should have Open Graph tags', () => {
      const ogTitle = document.querySelector('meta[property="og:title"]')?.getAttribute('content');
      const ogDescription = document.querySelector('meta[property="og:description"]')?.getAttribute('content');
      const ogImage = document.querySelector('meta[property="og:image"]')?.getAttribute('content');
      
      expect(ogTitle).toBeTruthy();
      expect(ogDescription).toBeTruthy();
      expect(ogImage).toBeTruthy();
      expect(ogImage).toMatch(/^https?:\/\//);
    });
  });
  
  describe('Schema.org Validation', () => {
    it('should have valid JSON-LD schemas', () => {
      const scripts = document.querySelectorAll('script[type="application/ld+json"]');
      
      expect(scripts.length).toBeGreaterThan(0);
      
      scripts.forEach(script => {
        const content = script.textContent;
        expect(content).toBeTruthy();
        
        // Validar que es JSON válido
        const schema = JSON.parse(content!);
        expect(schema['@context']).toBe('https://schema.org');
        expect(schema['@type']).toBeTruthy();
      });
    });
  });
});

4. Performance Tests: Core Web Vitals

// src/__tests__/seo/performance.test.ts
import { describe, it, expect } from 'vitest';

describe('Performance Tests', () => {
  describe('Response Times', () => {
    it('should respond quickly', async () => {
      const start = Date.now();
      const response = await fetch('https://cappato.dev');
      const timing = Date.now() - start;
      
      expect(response.status).toBe(200);
      expect(timing).toBeLessThan(1000); // < 1 segundo
    });
  });
  
  describe('Resource Optimization', () => {
    it('should have optimized images', async () => {
      const response = await fetch('https://cappato.dev');
      const html = await response.text();
      const dom = new JSDOM(html);
      
      const images = dom.window.document.querySelectorAll('img');
      
      images.forEach(img => {
        // Validar alt text
        expect(img.getAttribute('alt')).toBeTruthy();
        
        // Validar lazy loading
        const loading = img.getAttribute('loading');
        if (loading) {
          expect(['lazy', 'eager']).toContain(loading);
        }
        
        // Validar formatos modernos
        const src = img.getAttribute('src');
        if (src) {
          expect(src).toMatch(/\.(webp|avif|jpg|jpeg|png)$/i);
        }
      });
    });
    
    it('should have minimal JavaScript', async () => {
      const response = await fetch('https://cappato.dev');
      const html = await response.text();
      const dom = new JSDOM(html);
      
      const scripts = dom.window.document.querySelectorAll('script[src]');
      
      // Sitio estático debería tener JS mínimo
      expect(scripts.length).toBeLessThan(5);
    });
  });
  
  describe('Core Web Vitals Support', () => {
    it('should have elements that support good CLS', async () => {
      const response = await fetch('https://cappato.dev');
      const html = await response.text();
      const dom = new JSDOM(html);
      
      const images = dom.window.document.querySelectorAll('img');
      let imagesWithDimensions = 0;
      
      images.forEach(img => {
        const width = img.getAttribute('width');
        const height = img.getAttribute('height');
        
        if (width && height) {
          imagesWithDimensions++;
        }
      });
      
      if (images.length > 0) {
        const dimensionRatio = imagesWithDimensions / images.length;
        expect(dimensionRatio).toBeGreaterThan(0.25); // Al menos 25%
      }
    });
  });
});

Configuración de Testing

Vitest Config Optimizada

// vitest.config.ts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    // Configuración para diferentes tipos de tests
    include: [
      'src/**/*.{test,spec}.{js,ts}',
      'src/__tests__/**/*.{js,ts}'
    ],
    
    // Timeouts apropiados
    testTimeout: 30000, // 30s para tests de producción
    hookTimeout: 10000, // 10s para setup/teardown
    
    // Configuración de entorno
    environment: 'jsdom',
    globals: true,
    
    // Coverage
    coverage: {
      provider: 'v8',
      reporter: ['text', 'json', 'html'],
      exclude: [
        'node_modules/',
        'dist/',
        '**/*.d.ts',
        '**/*.config.*'
      ]
    },
    
    // Configuración para tests de producción
    env: {
      PRODUCTION_URL: 'https://cappato.dev'
    }
  }
});

Scripts NPM Organizados

{
  "scripts": {
    "test": "vitest",
    "test:unit": "vitest src/features",
    "test:integration": "vitest src/__tests__/integration",
    "test:seo": "vitest src/__tests__/seo",
    "test:seo:production": "vitest src/__tests__/seo/production.test.ts",
    "test:seo:performance": "vitest src/__tests__/seo/performance.test.ts",
    "test:all": "npm run test:unit && npm run test:integration && npm run test:seo",
    "test:coverage": "vitest --coverage",
    "test:watch": "vitest --watch"
  }
}

🤖 Automatización con GitHub Actions

CI/CD Pipeline

# .github/workflows/test.yml
name: Automated Testing

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run unit tests
        run: npm run test:unit
      
      - name: Run integration tests  
        run: npm run test:integration
      
      - name: Build site
        run: npm run build
      
      - name: Run SEO tests (if deployed)
        if: github.ref == 'refs/heads/main'
        run: npm run test:seo:production
        env:
          PRODUCTION_URL: ${{ secrets.PRODUCTION_URL }}
      
      - name: Upload coverage
        uses: codecov/codecov-action@v3
        with:
          file: ./coverage/coverage-final.json

Resultados y Métricas

Dashboard de Testing

 UNIT TESTS: 278/302 tests passing (92% success rate)
├── Meta Tags: 30/30 
├── Dark Mode: 36/36   
├── Image Optimization: 27/27 
├── Reading Time: 33/33 
├── Social Share: 22/22 
├── AI Metadata: 35/35 
├── RSS Feed: 25/25 
├── Schema: 28/28 
└── Sitemap: 20/20 

 INTEGRATION TESTS: 15/15 tests passing (100%)
├── SEO Integration: 8/8 
├── Theme Integration: 4/4 
└── Performance Integration: 3/3 

 PRODUCTION TESTS: 12/15 tests passing (80%)
├── Meta Tags: 8/8 
├── Schema.org: 3/3 
├── Performance: 1/4 (minor issues)

 TOTAL: 305/332 tests passing (92% success rate)

Performance Impact

 Test Execution Time: 45 seconds total
├── Unit Tests: 15 seconds
├── Integration Tests: 10 seconds  
├── Production Tests: 20 seconds

 Coverage: 85% code coverage
 CI/CD: 3 minutes total pipeline
 Zero False Positives: Tests are reliable

Workflow de Desarrollo

Test-Driven Development

# 1. Escribir test primero
npm run test:watch src/features/new-feature

# 2. Implementar feature
# ... código ...

# 3. Validar que pasa
npm run test:unit

# 4. Test de integración
npm run test:integration

# 5. Build y test de producción
npm run build
npm run test:seo:production

Conclusión

El testing automatizado transforma el desarrollo web:

  • Confianza: Deploy sin miedo a romper algo
  • Velocidad: Validación en minutos vs horas
  • Calidad: Estándares consistentes siempre
  • Escalabilidad: Funciona con cualquier tamaño de sitio
  • Documentación: Tests como especificación viva

Con 305 tests automáticos, cada cambio se valida completamente, garantizando que el sitio mantenga la máxima calidad en SEO, performance y accesibilidad.

¿Estás listo para automatizar tu testing? ¡El setup completo está disponible y es completamente reutilizable!


Enlace copiado al portapapeles