import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { render, act, waitFor } from '@testing-library/react';
// A voiceStore arrasta o AudioManager, que carrega um AudioWorklet inexistente
// no jsdom. Mesmo stub que os outros testes que tocam nesta store usam.
vi.mock('../../audio/AudioManager', () => ({
AudioManager: { getInstance: () => ({ resumeContext: vi.fn() }) },
}));
import { AndroidVoiceService } from './AndroidVoiceService';
import { useVoiceStore } from '../../stores/voiceStore';
const start = vi.fn(() => Promise.resolve());
const stop = vi.fn(() => Promise.resolve());
let hangupHandler: (() => void) | null = null;
function installCapacitor(platform = 'android') {
(globalThis as { Capacitor?: unknown }).Capacitor = {
isNativePlatform: () => true,
getPlatform: () => platform,
Plugins: {
VoiceForeground: {
start,
stop,
addListener: (_e: string, h: () => void) => {
hangupHandler = h;
return Promise.resolve({ remove: () => { hangupHandler = null; } });
},
},
},
};
}
describe('AndroidVoiceService', () => {
beforeEach(() => {
start.mockClear();
stop.mockClear();
hangupHandler = null;
useVoiceStore.setState({ isLiveKitConnected: false, currentVoiceChannelId: null });
});
afterEach(() => {
delete (globalThis as { Capacitor?: unknown }).Capacitor;
});
it('starts the service when a call connects and stops it when it ends', async () => {
installCapacitor();
render();
expect(start).not.toHaveBeenCalled();
act(() => {
useVoiceStore.setState({ isLiveKitConnected: true, currentVoiceChannelId: 'c-1' });
});
await waitFor(() => expect(start).toHaveBeenCalledTimes(1));
act(() => {
useVoiceStore.setState({ isLiveKitConnected: false, currentVoiceChannelId: null });
});
await waitFor(() => expect(stop).toHaveBeenCalledTimes(1));
});
it('starts on mount when a call is already in progress', async () => {
// A WebView pode recarregar no meio da chamada. Se o servico so subisse na
// proxima transicao, a call seguiria desprotegida ate alguem sair e entrar
// de novo — e cairia ao trancar a tela.
installCapacitor();
useVoiceStore.setState({ isLiveKitConnected: true, currentVoiceChannelId: 'c-1' });
render();
await waitFor(() => expect(start).toHaveBeenCalledTimes(1));
});
it('leaves the call when the notification hangup is tapped', async () => {
installCapacitor();
const disconnectFn = vi.fn(() => Promise.resolve());
useVoiceStore.setState({ disconnectFn });
render();
await waitFor(() => expect(hangupHandler).not.toBeNull());
act(() => hangupHandler!());
expect(disconnectFn).toHaveBeenCalled();
});
it('does nothing outside the Android app', async () => {
// Mesmo componente montado no navegador e no desktop: nao pode tocar em
// nada nativo nem quebrar.
render();
act(() => {
useVoiceStore.setState({ isLiveKitConnected: true, currentVoiceChannelId: 'c-1' });
});
expect(start).not.toHaveBeenCalled();
expect(stop).not.toHaveBeenCalled();
});
it('ignores a web build of Capacitor', async () => {
installCapacitor('web');
render();
act(() => {
useVoiceStore.setState({ isLiveKitConnected: true, currentVoiceChannelId: 'c-1' });
});
expect(start).not.toHaveBeenCalled();
});
});