import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { SttSettings } from './SttSettings'
import { getSttSettings, updateSttSettings } from '../../../api/stt'
vi.mock('../../../api/stt', () => ({
getSttSettings: vi.fn(),
updateSttSettings: vi.fn(),
}))
const SETTINGS = {
enabled: true,
providerKind: 'openai-compatible' as const,
mode: 'file' as const,
baseUrl: 'https://api.openai.com/v1',
apiKeyConfigured: true,
apiKeyRequired: true,
transcriptionEndpoint: '/audio/transcriptions',
transcriptionModel: 'gpt-4o-mini-transcribe',
language: 'pt',
insertMode: 'append' as const,
}
describe('SttSettings', () => {
beforeEach(() => {
vi.mocked(getSttSettings).mockReset()
vi.mocked(updateSttSettings).mockReset()
vi.mocked(getSttSettings).mockResolvedValue(SETTINGS)
vi.mocked(updateSttSettings).mockImplementation(async payload => ({
...SETTINGS,
...payload,
apiKeyConfigured: true,
language: payload.language ?? SETTINGS.language,
}))
})
it('uses the shared switch pattern for enabling voice transcription', async () => {
render()
const enableSwitch = await screen.findByRole('switch', { name: 'Enable voice transcription' })
expect(enableSwitch).toHaveAttribute('aria-checked', 'true')
expect(screen.queryByRole('checkbox')).not.toBeInTheDocument()
fireEvent.click(enableSwitch)
expect(enableSwitch).toHaveAttribute('aria-checked', 'false')
fireEvent.click(screen.getByRole('button', { name: 'Save transcription settings' }))
await waitFor(() => expect(updateSttSettings).toHaveBeenCalledWith(expect.objectContaining({ enabled: false })))
})
it('persists whether the STT provider requires an API key', async () => {
render()
const requireKeySwitch = await screen.findByRole('switch', { name: 'Require voice transcription API key' })
expect(requireKeySwitch).toHaveAttribute('aria-checked', 'true')
fireEvent.click(requireKeySwitch)
fireEvent.click(screen.getByRole('button', { name: 'Save transcription settings' }))
await waitFor(() =>
expect(updateSttSettings).toHaveBeenCalledWith(expect.objectContaining({ apiKeyRequired: false })),
)
})
it('shows one complete transcription configuration instead of split transcription cards', async () => {
render()
expect(await screen.findByText('Speech-to-text transcription')).toBeInTheDocument()
expect(screen.queryByText('Transcription provider')).not.toBeInTheDocument()
expect(screen.getByLabelText('Provider')).toHaveValue('openai-compatible')
expect(screen.getByLabelText('Base URL')).toHaveValue('https://api.openai.com/v1')
expect(screen.getByLabelText('Endpoint')).toHaveValue('/audio/transcriptions')
expect(screen.getByLabelText('Model')).toHaveValue('gpt-4o-mini-transcribe')
})
it('styles dropdown lists with the app select treatment', async () => {
render()
const insertMode = await screen.findByLabelText('Insert mode')
expect(insertMode).toHaveClass('appearance-none')
expect(insertMode.closest('[data-settings-select]')).toBeInTheDocument()
})
})