Files
flyer-crawler.projectium.com/src/features/voice-assistant/VoiceAssistant.test.tsx
Torben Sorensen f891da687b
Some checks failed
Deploy to Test Environment / deploy-to-test (push) Has been cancelled
Refactor tests to use mockClient for database interactions, improve error handling, and enhance modal functionality
- Updated personalization.db.test.ts to use mockClient for query calls in addWatchedItem tests.
- Simplified error handling in shopping.db.test.ts, ensuring clearer error messages.
- Added comprehensive tests for VoiceAssistant component, including rendering and interaction tests.
- Introduced useModal hook with tests to manage modal state effectively.
- Created deals.db.test.ts to test deals repository functionality with mocked database interactions.
- Implemented error handling tests for custom error classes in errors.db.test.ts.
- Developed googleGeocodingService.server.test.ts to validate geocoding service behavior with mocked fetch.
2025-12-14 01:12:33 -08:00

106 lines
3.8 KiB
TypeScript

// src/features/voice-assistant/VoiceAssistant.test.tsx
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { VoiceAssistant } from './VoiceAssistant';
import * as aiApiClient from '../../services/aiApiClient';
// Mock dependencies to isolate the component
vi.mock('../../services/aiApiClient', () => ({
startVoiceSession: vi.fn(),
}));
vi.mock('../../services/logger.client', () => ({
logger: {
info: vi.fn(),
debug: vi.fn(),
error: vi.fn(),
},
}));
vi.mock('../../components/icons/MicrophoneIcon', () => ({
MicrophoneIcon: () => <div data-testid="mic-icon" />,
}));
vi.mock('../../components/icons/XMarkIcon', () => ({
XMarkIcon: () => <div data-testid="x-icon" />,
}));
// Mock browser APIs that are not available in JSDOM
Object.defineProperty(window, 'AudioContext', {
writable: true,
value: vi.fn().mockImplementation(() => ({
createMediaStreamSource: vi.fn(() => ({
connect: vi.fn(),
})),
createScriptProcessor: vi.fn(() => ({
connect: vi.fn(),
disconnect: vi.fn(),
})),
close: vi.fn(),
})),
});
Object.defineProperty(navigator, 'mediaDevices', {
writable: true,
value: {
getUserMedia: vi.fn(),
},
});
describe('VoiceAssistant Component', () => {
const mockOnClose = vi.fn();
beforeEach(() => {
vi.clearAllMocks();
});
it('should not render when isOpen is false', () => {
const { container } = render(<VoiceAssistant isOpen={false} onClose={mockOnClose} />);
expect(container.firstChild).toBeNull();
});
it('should render correctly when isOpen is true', () => {
render(<VoiceAssistant isOpen={true} onClose={mockOnClose} />);
expect(screen.getByRole('heading', { name: /voice assistant/i })).toBeInTheDocument();
expect(screen.getByText('Click the mic to start')).toBeInTheDocument();
expect(screen.getByRole('button', { name: /close/i })).toBeInTheDocument();
expect(screen.getByTestId('mic-icon')).toBeInTheDocument();
});
it('should call onClose when the close button is clicked', () => {
render(<VoiceAssistant isOpen={true} onClose={mockOnClose} />);
fireEvent.click(screen.getByRole('button', { name: /close/i }));
expect(mockOnClose).toHaveBeenCalledTimes(1);
});
it('should call onClose when the overlay is clicked', () => {
render(<VoiceAssistant isOpen={true} onClose={mockOnClose} />);
// The overlay is the root div of the modal
fireEvent.click(screen.getByRole('dialog').parentElement!);
expect(mockOnClose).toHaveBeenCalledTimes(1);
});
it('should not call onClose when the modal content is clicked', () => {
render(<VoiceAssistant isOpen={true} onClose={mockOnClose} />);
fireEvent.click(screen.getByRole('heading', { name: /voice assistant/i }));
expect(mockOnClose).not.toHaveBeenCalled();
});
it('should call startVoiceSession when the microphone button is clicked in idle state', () => {
render(<VoiceAssistant isOpen={true} onClose={mockOnClose} />);
const micButton = screen.getByRole('button', { name: '' }); // The main button has no text
fireEvent.click(micButton);
expect(aiApiClient.startVoiceSession).toHaveBeenCalledTimes(1);
});
it('should display history and current transcripts', () => {
render(<VoiceAssistant isOpen={true} onClose={mockOnClose} />);
// This test is a bit more involved as it requires manipulating internal state.
// For a simple test, we can check that the container for history exists.
// A more advanced test would involve mocking the `startVoiceSession` callbacks.
const historyContainer = screen.getByRole('heading', { name: /voice assistant/i }).parentElement?.nextElementSibling;
expect(historyContainer).toBeInTheDocument();
expect(historyContainer).toBeEmptyDOMElement(); // Initially empty
});
});