Files
noteflow/client/e2e-native-mac/test-helpers.ts
2026-01-22 15:34:56 +00:00

142 lines
4.1 KiB
TypeScript

/**
* E2E Test Helpers for Native Mac Tests
*
* This module provides helpers for audio round-trip and post-processing tests.
* It communicates with the Rust backend via Tauri commands exposed for testing.
*/
import { invoke } from '@tauri-apps/api/core';
import { TauriCommands } from '../src/api/adapters/tauri/constants';
/**
* Test environment information returned by check_test_environment.
*/
export interface TestEnvironmentInfo {
/** Whether any input audio devices are available */
has_input_devices: boolean;
/** Whether a virtual audio device (BlackHole, Soundflower) is detected */
has_virtual_device: boolean;
/** List of available input device names */
input_devices: string[];
/** Whether the gRPC server is connected */
is_server_connected: boolean;
/** Whether audio tests can run (has devices + server) */
can_run_audio_tests: boolean;
}
/**
* Configuration for test audio injection.
*/
export interface TestAudioConfig {
/** Path to WAV file to inject */
wav_path: string;
/** Playback speed multiplier (1.0 = real-time, 2.0 = 2x speed) */
speed?: number;
/** Chunk duration in milliseconds */
chunk_ms?: number;
}
/**
* Result of test audio injection.
*/
export interface TestAudioResult {
/** Number of chunks sent */
chunks_sent: number;
/** Total duration in seconds */
duration_seconds: number;
/** Sample rate of the audio */
sample_rate: number;
}
/**
* Check if the test environment is properly configured for audio tests.
*/
export async function checkTestEnvironment(): Promise<TestEnvironmentInfo> {
return invoke<TestEnvironmentInfo>(TauriCommands.CHECK_TEST_ENVIRONMENT);
}
/**
* Inject test audio from a WAV file into the recording stream.
* This bypasses native audio capture for deterministic testing.
*
* @param meetingId - The meeting ID to inject audio into
* @param config - Audio injection configuration
*/
export async function injectTestAudio(
meetingId: string,
config: TestAudioConfig
): Promise<TestAudioResult> {
return invoke<TestAudioResult>(TauriCommands.INJECT_TEST_AUDIO, {
meeting_id: meetingId,
config,
});
}
/**
* Inject a test tone (sine wave) into the recording stream.
*
* @param meetingId - The meeting ID to inject audio into
* @param frequencyHz - Frequency of the sine wave in Hz
* @param durationSeconds - Duration of the tone in seconds
* @param sampleRate - Optional sample rate (default 16000)
*/
export async function injectTestTone(
meetingId: string,
frequencyHz: number,
durationSeconds: number,
sampleRate?: number
): Promise<TestAudioResult> {
return invoke<TestAudioResult>(TauriCommands.INJECT_TEST_TONE, {
meeting_id: meetingId,
frequency_hz: frequencyHz,
duration_seconds: durationSeconds,
sample_rate: sampleRate,
});
}
/**
* Test fixture paths for audio files.
* These paths are relative to the e2e-native-mac directory.
*/
export const TestFixtures = {
/** Path to short test tones (2 seconds) */
SHORT_TONES: 'fixtures/test-tones-2s.wav',
/** Path to longer test tones (10 seconds) */
LONG_TONES: 'fixtures/test-tones-10s.wav',
/** Path to simple sine wave (440Hz, 2 seconds) */
SINE_WAVE: 'fixtures/test-sine-440hz-2s.wav',
} as const;
/**
* Wait for a condition to be true with timeout.
*/
export async function waitForCondition(
condition: () => Promise<boolean> | boolean,
timeoutMs: number,
pollIntervalMs: number = 100
): Promise<boolean> {
const startTime = Date.now();
while (Date.now() - startTime < timeoutMs) {
if (await condition()) {
return true;
}
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
}
return false;
}
/**
* Calculate expected processing time for audio.
*
* @param durationSeconds - Audio duration in seconds
* @param realtimeFactor - Processing speed (1.0 = realtime, 0.5 = 2x faster)
*/
export function estimateProcessingTime(
durationSeconds: number,
realtimeFactor: number = 0.5
): number {
// Base processing time + some buffer
const baseBuffer = 5; // seconds
return Math.ceil(durationSeconds * realtimeFactor + baseBuffer);
}