Files

97 lines
4.4 KiB
TypeScript

import type { Page } from 'playwright'
import { describe, expect, it } from '../../src'
import { configureModuleHearing, configureOnboarding } from '../shared/configurations'
import { enableHearingPlaygroundMicrophone, readHearingPlaygroundTranscriptions } from '../shared/interactions'
import { appleSpeechAsr } from '../shared/providers'
// ROOT CAUSE:
//
// Chromium starts the non-looping fake microphone as soon as getUserMedia opens.
// Hearing requests microphone permission before the test starts monitoring, so a
// short fixture can finish before the Provider receives a speech segment. This
// fixture keeps 20 seconds of leading silence before the native transcription.
const input = new URL('../long-leading-silence/input.test.wav', import.meta.url)
const preflight = [
configureOnboarding(() => ({ completed: true })),
configureModuleHearing(async (context) => {
const isMacOS = await context.runtime.runtimePage.evaluate(() => 'platform' in window && window.platform === 'darwin')
context.skip(!isMacOS, 'Apple Speech requires macOS 26 or later.')
if (!isMacOS)
return undefined
return {
provider: appleSpeechAsr({ locale: 'en-US' }),
}
}),
]
async function waitForStoredLocale(page: Page, locale: string) {
await page.waitForFunction((expectedLocale) => {
const stored = localStorage.getItem('settings/providers/configured')
if (!stored)
return false
const providers = JSON.parse(stored) as Record<string, { config?: { locale?: string } }>
return providers['apple-speech-transcription']?.config?.locale === expectedLocale
}, locale)
}
describe('Apple Speech audio input', () => {
it('configures the native locale and transcribes through the Electron Provider', { input, preflight }, async ({ audio }) => {
const page = audio.runtimePage
audio.activatePage(page)
await page.evaluate(() => {
window.location.hash = '/settings/modules/hearing'
})
await page.waitForURL(/#\/settings\/modules\/hearing/)
await page.getByTestId('hearing-playground-monitor-toggle').waitFor({ state: 'visible', timeout: 60_000 })
const localeCombobox = page.getByTestId('apple-speech-locale').getByRole('combobox')
try {
await localeCombobox.waitFor({ state: 'visible', timeout: 10_000 })
}
catch (error) {
const diagnostics = await page.evaluate(() => ({
activeProvider: localStorage.getItem('settings/hearing/active-provider'),
configuredProviders: localStorage.getItem('settings/providers/configured'),
localeFieldCount: document.querySelectorAll('[data-testid="apple-speech-locale"]').length,
localeTextVisible: document.body.textContent?.includes('Locale') ?? false,
}))
throw new Error(`Apple Speech locale field is unavailable: ${JSON.stringify(diagnostics)}`, { cause: error })
}
await localeCombobox.click()
const localeOptions = await page.getByRole('option').allTextContents()
expect(localeOptions.some(option => option.includes('en-US'))).toBe(true)
const zhCNOption = page.getByRole('option').filter({ hasText: 'zh-CN' }).first()
await zhCNOption.click()
await waitForStoredLocale(page, 'zh-CN')
await localeCombobox.click()
const enUSOption = page.getByRole('option').filter({ hasText: 'en-US' }).first()
await enUSOption.click()
await waitForStoredLocale(page, 'en-US')
await enableHearingPlaygroundMicrophone(page)
try {
await readHearingPlaygroundTranscriptions(page, 1)
}
catch (error) {
const diagnostics = await page.evaluate(() => ({
activeModel: localStorage.getItem('settings/hearing/active-model'),
activeProvider: localStorage.getItem('settings/hearing/active-provider'),
configuredProviders: localStorage.getItem('settings/providers/configured'),
piniaActionEvents: window.__airiAudioInputE2E?.piniaActionEvents ?? [],
probeInstalled: Boolean(window.__airiAudioInputE2E),
streamingTranscriptionReady: window.__airiAudioInputE2E?.streamingTranscriptionReady ?? false,
streamingTranscriptionUpdates: window.__airiAudioInputE2E?.streamingTranscriptionUpdates ?? [],
vadReady: window.__airiAudioInputE2E?.vadReady ?? false,
}))
throw new Error(`Apple Speech did not produce a transcript: ${JSON.stringify(diagnostics)}`, { cause: error })
}
await expect(audio).toHaveTranscriptions([
['Just let go.'],
], { match: 'contains' })
})
})