Saltar al contenido principal
Esta guía cubre cómo testear tu integración del SDK de SofIA, desde verificar la configuración básica hasta automatizar tests en pipelines CI/CD.

Resumen de estrategia de testing

NivelQué testearHerramientas
Verificación manualEl componente renderiza, las props son aceptadas, los logs de debug aparecenBrowser DevTools
Tests unitariosFunciones de callback, transformaciones de datos, validación de templatesVitest, Jest
Tests de integraciónMontaje de componentes, paso de props, manejo de eventosTesting Library, Cypress
Tests E2EFlujo completo desde transcripción hasta reportePlaywright, Cypress

Checklist de verificación manual

Antes de escribir tests automatizados, verifica lo básico con debug="true":
1

El componente renderiza

Abre tu página y confirma que el widget de SofIA aparece. Revisa la consola buscando [Sofia SDK] Configuration Error — si está ausente, las props son válidas.
2

El template se carga

Busca [Sofia SDK] Settings - Template loaded: {n} fields en la consola. Si n coincide con tu cantidad esperada de campos, el template se parseó correctamente.
3

El botón de generar aparece

Si tanto template como templateid están configurados, el botón de generar debería ser visible. Si falta, revisa los logs de validación de template.
4

handleReport se ejecuta

Haz clic en Generar después de una conversación y verifica que tu callback handleReport recibe datos. Loguea la salida con JSON.stringify(report, null, 2).

Testing unitario de callbacks

Testea tus funciones de callback independientemente del SDK. Estos tests verifican que tu aplicación procesa correctamente los datos que SofIA entrega.

Testing de handleReport

import { describe, it, expect, vi } from 'vitest';

// El handler de reportes de tu aplicación
function processReport(report) {
  if (!report || Object.keys(report).length === 0) {
    throw new Error('Reporte vacío recibido');
  }

  return {
    diagnosis: report.diagnostico_principal || null,
    symptoms: report.sintomas || [],
    timestamp: new Date().toISOString(),
  };
}

describe('processReport', () => {
  it('debería extraer diagnóstico del reporte', () => {
    const mockReport = {
      diagnostico_principal: 'Diabetes Mellitus Tipo 2',
      sintomas: ['poliuria', 'polidipsia', 'fatiga'],
    };

    const result = processReport(mockReport);

    expect(result.diagnosis).toBe('Diabetes Mellitus Tipo 2');
    expect(result.symptoms).toHaveLength(3);
    expect(result.timestamp).toBeDefined();
  });

  it('debería manejar campos opcionales faltantes', () => {
    const mockReport = {
      diagnostico_principal: 'Hipertensión',
    };

    const result = processReport(mockReport);

    expect(result.diagnosis).toBe('Hipertensión');
    expect(result.symptoms).toEqual([]);
  });

  it('debería lanzar error con reporte vacío', () => {
    expect(() => processReport({})).toThrow('Reporte vacío recibido');
  });
});

Testing de validación de template

import { describe, it, expect } from 'vitest';

function isValidTemplate(template) {
  try {
    const schema = typeof template === 'string' ? JSON.parse(template) : template;
    return (
      schema.$schema === 'http://json-schema.org/draft-07/schema#' &&
      schema.type === 'object' &&
      schema.properties &&
      Object.keys(schema.properties).length > 0
    );
  } catch {
    return false;
  }
}

describe('isValidTemplate', () => {
  it('debería aceptar JSON Schema Draft-07 válido', () => {
    const template = JSON.stringify({
      $schema: 'http://json-schema.org/draft-07/schema#',
      title: 'test',
      type: 'object',
      properties: {
        diagnostico: { type: 'string' },
      },
    });

    expect(isValidTemplate(template)).toBe(true);
  });

  it('debería rechazar esquema sin properties', () => {
    const template = JSON.stringify({
      $schema: 'http://json-schema.org/draft-07/schema#',
      type: 'object',
      properties: {},
    });

    expect(isValidTemplate(template)).toBe(false);
  });

  it('debería rechazar JSON inválido', () => {
    expect(isValidTemplate('{invalido')).toBe(false);
  });
});

Testing de integración

Testing de montaje del componente (Vanilla JS)

import { describe, it, expect, beforeEach, afterEach } from 'vitest';

describe('Montaje del SDK de SofIA', () => {
  let container;

  beforeEach(() => {
    container = document.createElement('div');
    document.body.appendChild(container);
  });

  afterEach(() => {
    container.remove();
  });

  it('debería montar con props requeridas', async () => {
    container.innerHTML = `
      <sofia-sdk
        baseurl="https://api.test.com"
        wssurl="wss://ws.test.com"
        apikey="test-key"
        userid="doctor-1"
        patientid="paciente-1"
      ></sofia-sdk>
    `;

    const sofia = container.querySelector('sofia-sdk');
    expect(sofia).not.toBeNull();
  });

  it('debería aceptar callback handleReport', async () => {
    container.innerHTML = '<sofia-sdk></sofia-sdk>';
    const sofia = container.querySelector('sofia-sdk');

    const handleReport = vi.fn();
    sofia.handleReport = handleReport;

    expect(sofia.handleReport).toBe(handleReport);
  });
});

Testing con React

import { render, screen } from '@testing-library/react';
import { useRef, useEffect } from 'react';
import { describe, it, expect, vi } from 'vitest';

function SofiaWrapper({ onReport, template, templateId }) {
  const ref = useRef(null);

  useEffect(() => {
    if (ref.current) {
      ref.current.handleReport = onReport;
    }
  }, [onReport]);

  return (
    <sofia-sdk
      ref={ref}
      baseurl="https://api.test.com"
      wssurl="wss://ws.test.com"
      apikey="test-key"
      userid="doctor-1"
      patientid="paciente-1"
      template={template}
      templateid={templateId}
    />
  );
}

describe('SofiaWrapper', () => {
  it('debería renderizar sin errores', () => {
    const handleReport = vi.fn();
    render(<SofiaWrapper onReport={handleReport} />);

    // El componente renderiza sin lanzar excepción
    expect(document.querySelector('sofia-sdk')).not.toBeNull();
  });

  it('debería asignar callback handleReport', () => {
    const handleReport = vi.fn();
    render(<SofiaWrapper onReport={handleReport} />);

    const sofia = document.querySelector('sofia-sdk');
    expect(sofia.handleReport).toBe(handleReport);
  });
});

Testing con Angular

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { SofiaComponent } from './sofia.component';

describe('SofiaComponent', () => {
  let component: SofiaComponent;
  let fixture: ComponentFixture<SofiaComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [SofiaComponent],
      schemas: [CUSTOM_ELEMENTS_SCHEMA], // Requerido para web components
    }).compileComponents();

    fixture = TestBed.createComponent(SofiaComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('debería crearse', () => {
    expect(component).toBeTruthy();
  });

  it('debería tener el elemento sofia-sdk en el template', () => {
    const compiled = fixture.nativeElement;
    expect(compiled.querySelector('sofia-sdk')).not.toBeNull();
  });
});

Mockear el SDK para CI/CD

En entornos CI donde el SDK no puede conectarse a servidores reales, mockea el web component para testear tu lógica de integración:
// test/mocks/sofia-sdk-mock.js

class MockSofiaSDK extends HTMLElement {
  constructor() {
    super();
    this._handleReport = null;
  }

  set handleReport(fn) {
    this._handleReport = fn;
  }

  get handleReport() {
    return this._handleReport;
  }

  // Simular generación de reporte para testing
  simulateReport(report) {
    if (this._handleReport) {
      this._handleReport(report);
    }
    this.dispatchEvent(new CustomEvent('handle-report', {
      detail: report,
      bubbles: true,
      composed: true,
    }));
  }
}

// Registrar mock antes de los tests
if (!customElements.get('sofia-sdk')) {
  customElements.define('sofia-sdk', MockSofiaSDK);
}
Usando el mock en tests:
import './mocks/sofia-sdk-mock.js';
import { describe, it, expect, vi } from 'vitest';

describe('Procesamiento de reportes con mock del SDK', () => {
  it('debería procesar reporte simulado', () => {
    const container = document.createElement('div');
    container.innerHTML = '<sofia-sdk></sofia-sdk>';
    document.body.appendChild(container);

    const sofia = container.querySelector('sofia-sdk');
    const handleReport = vi.fn();
    sofia.handleReport = handleReport;

    // Simular que el SDK genera un reporte
    sofia.simulateReport({
      diagnostico_principal: 'Bronquitis aguda',
      sintomas: ['tos', 'fiebre'],
    });

    expect(handleReport).toHaveBeenCalledWith({
      diagnostico_principal: 'Bronquitis aguda',
      sintomas: ['tos', 'fiebre'],
    });

    container.remove();
  });
});

Testing de eventos personalizados

El SDK emite eventos DOM personalizados que pueden testearse directamente:
import { describe, it, expect, vi } from 'vitest';

describe('Eventos personalizados del SDK', () => {
  it('debería escuchar evento handle-report', () => {
    const container = document.createElement('div');
    container.innerHTML = '<sofia-sdk></sofia-sdk>';
    document.body.appendChild(container);

    const sofia = container.querySelector('sofia-sdk');
    const listener = vi.fn();

    sofia.addEventListener('handle-report', (e) => {
      listener(e.detail);
    });

    // Simular el evento
    sofia.dispatchEvent(new CustomEvent('handle-report', {
      detail: { diagnostico: 'Test' },
      bubbles: true,
      composed: true,
    }));

    expect(listener).toHaveBeenCalledWith({ diagnostico: 'Test' });

    container.remove();
  });
});

Configuración del entorno de testing

Configuración de Vitest

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

export default defineConfig({
  test: {
    environment: 'jsdom', // Requerido para APIs del DOM
    globals: true,
    setupFiles: ['./test/setup.js'],
  },
});

Archivo de setup

// test/setup.js

// Registrar mock del SDK antes de todos los tests
import './mocks/sofia-sdk-mock.js';

// Suprimir warnings de consola del SDK en la salida de tests
const originalWarn = console.warn;
console.warn = (...args) => {
  if (args[0]?.includes?.('[Sofia SDK]')) return;
  originalWarn(...args);
};
Enfoca tus tests automatizados en tu código de integración (callbacks, transformaciones de datos, actualizaciones de UI), no en los internos del SDK. El SDK es testeado internamente con más de 160 casos de test. Tus tests deberían verificar que tu aplicación procesa correctamente los datos que SofIA entrega.

Próximos pasos

  1. Solución de Problemas — soluciones para problemas comunes
  2. Referencia de Errores — catálogo completo de mensajes de error del SDK
  3. Guía de Migración — actualizar de v0.x a v1.0