Files
perler-beads-webui/tests/notifications.test.ts
T

17 lines
3.2 KiB
TypeScript

import test from 'node:test'
import assert from 'node:assert/strict'
import { CallTracker, announcement, SpeechQueue, selectVoice } from '../src/notifications/core.ts'
import type { Task } from '../src/contract.ts'
function task(id:string,status='pending'):Task{return{id,status,tableCode:'T01',kind:'help',note:'私人备注不要播报',createdAt:'2026-10-03T09:00:00Z',updatedAt:''}}
test('首次只建基线;重复轮询和已处理任务不播报',()=>{let t=new CallTracker();assert.deepEqual(t.accept([task('old')]),[]);assert.deepEqual(t.accept([task('old'),task('new'),task('done','accepted')]).map(x=>x.id),['new']);assert.deepEqual(t.accept([task('new')]),[]);assert.deepEqual(t.accept([task('old')]),[])})
test('静音快照仍去重;重置后历史任务不重播',()=>{let t=new CallTracker();t.accept([]);t.accept([task('muted')]);assert.deepEqual(t.accept([task('muted'),task('new')]).map(x=>x.id),['new']);t.reset();assert.deepEqual(t.accept([task('new')]),[])})
test('播报只包括桌位与服务类型,不包含顾客备注',()=>{assert.equal(announcement(task('x')),'T01 桌,呼叫员工。');assert.equal(announcement({...task('x'),kind:'refill'}),'T01 桌,需要补充豆料。')})
test('仅选择本机中文语音,优先普通话',()=>{let remote={lang:'zh-CN',localService:false};let cantonese={lang:'zh-HK',localService:true};let mandarin={lang:'zh-CN',localService:true};assert.equal(selectVoice([remote,cantonese,mandarin]),mandarin);assert.equal(selectVoice([remote,{lang:'en-US',localService:true}]),undefined)})
function harness(){let spoken:string[]=[];let callbacks:Array<(error?:string)=>void>=[];let cancelled=0;let errors:string[]=[];let q=new SpeechQueue({speak(text,done){spoken.push(text);callbacks.push(done)},cancel(){cancelled++}},e=>errors.push(e));return {q,spoken,callbacks,errors,get cancelled(){return cancelled}}}
test('队列串行播报且待接单任务被取消后不再播报',()=>{let h=harness();h.q.add([task('a'),task('b'),task('c')]);assert.equal(h.spoken.length,1);h.q.retain(new Set(['a','c']));h.callbacks[0]();assert.equal(h.spoken.length,2);h.q.stop();h.callbacks[1]();assert.equal(h.spoken.length,2);assert.equal(h.cancelled,1)})
test('关闭后的旧回调不影响新队列;播报错误清空并上报',()=>{let h=harness();h.q.add([task('a')]);h.q.stop();h.q.add([task('b'),task('c')]);h.callbacks[0]();assert.equal(h.spoken.length,2);h.callbacks[1]('播放被浏览器阻止');assert.deepEqual(h.errors,['播放被浏览器阻止']);assert.equal(h.spoken.length,2)})
test('队列过载明确提示,不产生无限语音积压',()=>{let h=harness();h.q.add(Array.from({length:33},(_,i)=>task(String(i))));assert.equal(h.spoken.length,0);assert.equal(h.errors.length,1)})
test('试听期间收到的新呼叫排队保留,已开启时试听也不清空任务',()=>{let h=harness();let completed=0;h.q.preview('语音开启',()=>completed++);h.q.add([task('new')]);h.callbacks[0]();assert.equal(completed,1);assert.equal(h.spoken[1],'T01 桌,呼叫员工。');h.q.preview('再次试听',()=>completed++);h.q.add([task('second')]);h.callbacks[1]();assert.equal(h.spoken[2],'再次试听');h.callbacks[2]();assert.equal(h.spoken.length,4);h.q.stop()})