import { afterEach, describe, expect, it, vi } from 'vitest'; import { clearTranslationMemory, isTranslationPairSupported, translateText } from './translation'; class FakeWorker { onmessage: ((event: MessageEvent) => void) | null = null; onerror: (() => void) | null = null; postMessage(request: { id: number; text: string }) { queueMicrotask(() => { this.onmessage?.({ data: { type: 'result', id: request.id, text: `translated:${request.text}` }, } as MessageEvent); }); } terminate() {} } describe('translateText', () => { afterEach(() => { vi.unstubAllGlobals(); clearTranslationMemory(); }); it('keeps the message local and never calls fetch', async () => { const fetchSpy = vi.fn(); const received: string[] = []; class RecordingWorker extends FakeWorker { postMessage(request: { id: number; text: string }) { received.push(request.text); super.postMessage(request); } } vi.stubGlobal('fetch', fetchSpy); vi.stubGlobal('Worker', RecordingWorker); clearTranslationMemory(); const result = await translateText({ id: 'message-1', text: 'SENTINEL-MESSAGE-TEXT', source: 'en', target: 'es', }); expect(result).toBe('translated:SENTINEL-MESSAGE-TEXT'); expect(received).toEqual(['SENTINEL-MESSAGE-TEXT']); expect(fetchSpy).not.toHaveBeenCalled(); }); it('returns the input when source and target match', async () => { const result = await translateText({ id: 'message-2', text: 'same language', source: 'en', target: 'en', }); expect(result).toBe('same language'); }); it('supports all app language pairs (M2M100 + Basque Opus)', () => { expect(isTranslationPairSupported('ca', 'en')).toBe(true); expect(isTranslationPairSupported('eu', 'en')).toBe(true); expect(isTranslationPairSupported('hi', 'es')).toBe(true); expect(isTranslationPairSupported('en', 'en')).toBe(false); }); });