Pruebas de voz: cubrir también el camino feliz

La suite probaba los fallos (recolector, motor pausado, bloqueo, voz lenta)
pero no que HABLE BIEN: qué texto dice exactamente, con qué voz, y cómo
pronuncia. Un anuncio que suena pero dice «MaríA GóMez» o elige la voz remota
teniendo la local también es un fallo, y no estaba vigilado.

Cuatro casos nuevos: el texto deletrea el código y cierra con el destino, las
tildes capitalizan bien, se prefiere la voz local sobre la remota es-CO, y sin
nombre de paciente igual habla. 13 verificaciones en total.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-08-23 11:47:59 -05:00
co-authored by Claude Fable 5
parent 26efe10ae6
commit 9ebe6990a5
+53
View File
@@ -131,6 +131,59 @@ function escenario({ bloqueado = false, msArranque = 20 } = {}) {
e.beacons.length >= 2);
}
// ── 6. El camino feliz: habla bien ──────────────────────────────────────
// Qué dice exactamente, y con qué voz. Un anuncio que "funciona" pero
// pronuncia mal el nombre o elige la voz equivocada también es un fallo.
{
// Instrumentación directa: escenario propio con captura de la locución
let t = 0; const cola = [];
const st = (fn, ms) => cola.push({ t: t + ms, fn });
class Utt {
constructor(x) { this.texto = x; this.l = {}; }
addEventListener(e, f) { (this.l[e] = this.l[e] || []).push(f); }
emit(e, a) { (this.l[e] || []).forEach(f => f(a || {})); }
}
let capturada = null;
const synth = {
speaking: false, pending: false, pause() {}, cancel() {}, resume() {},
getVoices: () => [
{ name: 'Microsoft Sabina', lang: 'es-MX', localService: true },
{ name: 'Google español de Colombia', lang: 'es-CO', localService: false },
],
speak(u) {
capturada = u;
st(() => u.emit('start'), 10);
st(() => u.emit('end'), 4000);
},
};
const api = new Function(
'window', 'setTimeout', 'setInterval', 'SpeechSynthesisUtterance',
'localStorage', 'navigator', 'Blob', 'Date',
cuerpo + '; return { anunciarTurno };'
)({ speechSynthesis: synth }, st, () => {}, Utt,
{ getItem: () => '[]', setItem: () => {} },
{ sendBeacon: () => {} }, function Blob() {},
Object.assign(function () { return { toISOString: () => '2026-01-01T00:00:00' }; }, { now: () => t }));
api.anunciarTurno('A01', 'Consultorio 2', 'maría fernanda gómez', () => {});
let n = 0;
while (cola.length && n++ < 200) { cola.sort((a, b) => a.t - b.t); const ev = cola.shift(); t = ev.t; ev.fn(); }
ok('el texto deletrea el código y termina con el destino',
capturada && capturada.texto === 'Turno A 0 1, María Fernanda Gómez, pase a Consultorio 2');
ok('los nombres con tilde salen bien capitalizados (María, no MaríA)',
capturada && capturada.texto.includes('María Fernanda Gómez'));
ok('elige la voz LOCAL (Sabina) aunque la remota es-CO esté disponible',
capturada && capturada.voice && capturada.voice.name === 'Microsoft Sabina');
ok('sin nombre de paciente igual habla: solo código y destino', (() => {
capturada = null;
api.anunciarTurno('B02', 'Recepción 1', null, () => {});
let m = 0;
while (cola.length && m++ < 200) { cola.sort((a, b) => a.t - b.t); const ev = cola.shift(); t = ev.t; ev.fn(); }
return capturada && capturada.texto === 'Turno B 0 2, pase a Recepción 1';
})());
}
console.log();
console.log(fallos === 0 ? 'Todo correcto.' : fallos + ' pruebas fallaron.');
process.exit(fallos === 0 ? 0 : 1);