Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | 'use client' import { useCallback, useState } from 'react' import { useAudioManagerInstance } from '@/contexts/AudioManagerContext' import { TestCard, type TestStatus } from '../TestCard' import { useTestLog } from '../useTestLog' import { css } from '../../../../../styled-system/css' const WORDS = ['One', 'Two', 'Three', 'Four', 'Five'] function LabButton({ onClick, disabled, children, }: { onClick: () => void disabled?: boolean children: React.ReactNode }) { return ( <button onClick={onClick} disabled={disabled} className={css({ fontSize: '12px', padding: '6px 12px', borderRadius: '6px', border: '1px solid #30363d', backgroundColor: '#21262d', color: '#c9d1d9', cursor: 'pointer', _hover: { backgroundColor: '#30363d' }, _disabled: { opacity: 0.5, cursor: 'not-allowed' }, })} > {children} </button> ) } export function RapidFireTest() { const manager = useAudioManagerInstance() const { entries, log, clear } = useTestLog() const [status, setStatus] = useState<TestStatus>('idle') const handleNoAwait = useCallback(() => { setStatus('running') log('info', 'Fire 5x (no await) — calling speak() in sync loop') let completed = 0 for (let i = 0; i < WORDS.length; i++) { const word = WORDS[i] const start = performance.now() log('info', ` speak("${word}") called`) manager.speak({ say: { en: word }, tone: 'tutorial-instruction' }).then( () => { const elapsed = (performance.now() - start).toFixed(0) completed++ log('success', ` speak("${word}") resolved (${elapsed}ms)`) if (completed === WORDS.length) { setStatus('pass') } }, (err) => { completed++ log('error', ` speak("${word}") rejected`, String(err)) setStatus('fail') } ) } }, [manager, log]) const handleWithGaps = useCallback(() => { setStatus('running') log('info', 'Fire 5x (50ms gaps) — setTimeout between calls') let completed = 0 WORDS.forEach((word, i) => { setTimeout(() => { const start = performance.now() log('info', ` speak("${word}") called at +${i * 50}ms`) manager.speak({ say: { en: word }, tone: 'tutorial-instruction' }).then( () => { const elapsed = (performance.now() - start).toFixed(0) completed++ log('success', ` speak("${word}") resolved (${elapsed}ms)`) if (completed === WORDS.length) { setStatus('pass') } }, (err) => { completed++ log('error', ` speak("${word}") rejected`, String(err)) setStatus('fail') } ) }, i * 50) }) }, [manager, log]) const handleAwaited = useCallback(async () => { setStatus('running') log('info', 'Fire 5x (awaited) — sequential await each speak()') for (const word of WORDS) { const start = performance.now() log('info', ` speak("${word}") called`) try { await manager.speak({ say: { en: word }, tone: 'tutorial-instruction' }) const elapsed = (performance.now() - start).toFixed(0) log('success', ` speak("${word}") resolved (${elapsed}ms)`) } catch (err) { log('error', ` speak("${word}") rejected`, String(err)) setStatus('fail') return } } setStatus('pass') log('success', 'All 5 sequential speaks completed') }, [manager, log]) return ( <TestCard title="Test 2: Rapid-Fire Overlap" description="What happens when speak() is called while a previous call is still playing?" status={status} entries={entries} onClear={clear} > <LabButton onClick={handleNoAwait}>Fire 5x (no await)</LabButton> <LabButton onClick={handleWithGaps}>Fire 5x (50ms gaps)</LabButton> <LabButton onClick={handleAwaited}>Fire 5x (awaited)</LabButton> </TestCard> ) } |