All files / web/src/components/admin/tts-lab/tests StrictModeTest.tsx

0% Statements 0/122
0% Branches 0/1
0% Functions 0/1
0% Lines 0/122

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                                                                                                                                                                                                                                                     
'use client'

import React, { useCallback, useEffect, useRef, useState } from 'react'
import { useAudioManagerInstance } from '@/contexts/AudioManagerContext'
import { useTTS } from '@/hooks/useTTS'
import { TestCard, type TestStatus } from '../TestCard'
import { useTestLog, type UseTestLogReturn } from '../useTestLog'
import { css } from '../../../../../styled-system/css'

function LabButton({ onClick, children }: { onClick: () => void; children: React.ReactNode }) {
  return (
    <button
      onClick={onClick}
      className={css({
        fontSize: '12px',
        padding: '6px 12px',
        borderRadius: '6px',
        border: '1px solid #30363d',
        backgroundColor: '#21262d',
        color: '#c9d1d9',
        cursor: 'pointer',
        _hover: { backgroundColor: '#30363d' },
      })}
    >
      {children}
    </button>
  )
}

/** Inner child that uses useTTS — rendered inside StrictMode wrapper */
function StrictModeChild({ log }: { log: UseTestLogReturn['log'] }) {
  const mountCount = useRef(0)

  const speak = useTTS({ say: { en: 'Strict mode test' }, tone: 'tutorial-instruction' })

  useEffect(() => {
    mountCount.current++
    log('info', `useEffect fired (mount #${mountCount.current})`)
    return () => {
      log('info', `useEffect cleanup (mount #${mountCount.current})`)
    }
  }, [log])

  const handleSpeak = useCallback(async () => {
    log('info', 'Speak button clicked')
    try {
      await speak()
      log('success', 'Speak completed')
    } catch (err) {
      log('error', 'Speak failed', String(err))
    }
  }, [speak, log])

  return (
    <div
      className={css({
        padding: '8px 12px',
        border: '1px dashed #30363d',
        borderRadius: '6px',
        display: 'flex',
        alignItems: 'center',
        gap: '8px',
      })}
    >
      <span className={css({ fontSize: '11px', color: '#8b949e' })}>StrictMode Child</span>
      <LabButton onClick={handleSpeak}>Speak</LabButton>
    </div>
  )
}

export function StrictModeTest() {
  const manager = useAudioManagerInstance()
  const { entries, log, clear } = useTestLog()
  const [status, setStatus] = useState<TestStatus>('idle')
  const [mounted, setMounted] = useState(false)

  const handleToggleMount = useCallback(() => {
    setMounted((prev) => {
      const next = !prev
      log('info', next ? 'Mounting StrictMode child' : 'Unmounting StrictMode child')
      return next
    })
  }, [log])

  const handleCheckCollection = useCallback(() => {
    const collection = manager.getCollection()
    const matching = collection.filter(
      (c) => c.clipId.startsWith('h-') && c.say?.en === 'Strict mode test'
    )
    log('info', `Collection entries matching "Strict mode test": ${matching.length}`)
    if (matching.length === 1) {
      log('success', 'Exactly 1 entry — register() is idempotent')
      setStatus('pass')
    } else if (matching.length === 0) {
      log('warn', 'No matching entries found (is child mounted?)')
    } else {
      log('error', `Found ${matching.length} entries — duplicate registration!`)
      setStatus('fail')
    }
    log('info', `Total collection size: ${collection.length}`)
  }, [manager, log])

  return (
    <TestCard
      title="Test 3: Strict Mode Double Mount"
      description="Does dev-mode double-mount cause double registration or double playback? (Dev mode only)"
      status={status}
      entries={entries}
      onClear={clear}
    >
      <LabButton onClick={handleToggleMount}>
        {mounted ? 'Unmount Component' : 'Mount Component'}
      </LabButton>
      <LabButton onClick={handleCheckCollection}>Check Collection</LabButton>
      {mounted && (
        <React.StrictMode>
          <StrictModeChild log={log} />
        </React.StrictMode>
      )}
    </TestCard>
  )
}