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
| Nivel | Qué testear | Herramientas |
|---|
| Verificación manual | El componente renderiza, las props son aceptadas, los logs de debug aparecen | Browser DevTools |
| Tests unitarios | Funciones de callback, transformaciones de datos, validación de templates | Vitest, Jest |
| Tests de integración | Montaje de componentes, paso de props, manejo de eventos | Testing Library, Cypress |
| Tests E2E | Flujo completo desde transcripción hasta reporte | Playwright, Cypress |
Checklist de verificación manual
Antes de escribir tests automatizados, verifica lo básico con debug="true":
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.
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.
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.
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
- Solución de Problemas — soluciones para problemas comunes
- Referencia de Errores — catálogo completo de mensajes de error del SDK
- Guía de Migración — actualizar de v0.x a v1.0