197 lines
7.4 KiB
JavaScript
197 lines
7.4 KiB
JavaScript
// frontend/tests/api.test.js
|
|
|
|
// Import the function to test (adjust path if your structure is different)
|
|
// We might need to configure Jest or use Babel for ES module syntax if this causes issues.
|
|
import { getSpeakers, addSpeaker, deleteSpeaker, generateDialog } from '../js/api.js';
|
|
|
|
// Mock the global fetch function
|
|
global.fetch = jest.fn();
|
|
|
|
const API_BASE_URL = 'http://localhost:8000/api'; // Centralize for all tests
|
|
|
|
describe('API Client - getSpeakers', () => {
|
|
beforeEach(() => {
|
|
// Clear all instances and calls to constructor and all methods:
|
|
fetch.mockClear();
|
|
});
|
|
|
|
it('should fetch speakers successfully', async () => {
|
|
const mockSpeakers = [{ id: '1', name: 'Speaker 1' }, { id: '2', name: 'Speaker 2' }];
|
|
fetch.mockResolvedValueOnce({
|
|
ok: true,
|
|
json: async () => mockSpeakers,
|
|
});
|
|
|
|
const speakers = await getSpeakers();
|
|
expect(fetch).toHaveBeenCalledTimes(1);
|
|
expect(fetch).toHaveBeenCalledWith(`${API_BASE_URL}/speakers`);
|
|
expect(speakers).toEqual(mockSpeakers);
|
|
});
|
|
|
|
it('should throw an error if the network response is not ok', async () => {
|
|
fetch.mockResolvedValueOnce({
|
|
ok: false,
|
|
statusText: 'Not Found',
|
|
json: async () => ({ detail: 'Speakers not found' }) // Simulate FastAPI error response
|
|
});
|
|
|
|
await expect(getSpeakers()).rejects.toThrow('Failed to fetch speakers: Speakers not found');
|
|
expect(fetch).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('should throw a generic error if parsing error response fails', async () => {
|
|
fetch.mockResolvedValueOnce({
|
|
ok: false,
|
|
statusText: 'Internal Server Error',
|
|
json: async () => { throw new Error('Failed to parse error JSON'); } // Simulate error during .json()
|
|
});
|
|
|
|
await expect(getSpeakers()).rejects.toThrow('Failed to fetch speakers: Internal Server Error');
|
|
expect(fetch).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('should throw an error if fetch itself fails (network error)', async () => {
|
|
fetch.mockRejectedValueOnce(new TypeError('Network failed'));
|
|
|
|
await expect(getSpeakers()).rejects.toThrow('Network failed'); // This will be the original fetch error
|
|
expect(fetch).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|
|
|
|
describe('API Client - addSpeaker', () => {
|
|
beforeEach(() => {
|
|
fetch.mockClear();
|
|
});
|
|
|
|
it('should add a speaker successfully', async () => {
|
|
const mockFormData = new FormData(); // In a real scenario, this would have data
|
|
mockFormData.append('name', 'Test Speaker');
|
|
// mockFormData.append('audio_sample_file', new File([''], 'sample.wav')); // File creation in Node test needs more setup or a mock
|
|
|
|
const mockResponse = { id: '3', name: 'Test Speaker', message: 'Speaker added successfully' };
|
|
fetch.mockResolvedValueOnce({
|
|
ok: true,
|
|
json: async () => mockResponse,
|
|
});
|
|
|
|
const result = await addSpeaker(mockFormData);
|
|
expect(fetch).toHaveBeenCalledTimes(1);
|
|
expect(fetch).toHaveBeenCalledWith(`${API_BASE_URL}/speakers`, {
|
|
method: 'POST',
|
|
body: mockFormData,
|
|
});
|
|
expect(result).toEqual(mockResponse);
|
|
});
|
|
|
|
it('should throw an error if adding a speaker fails', async () => {
|
|
const mockFormData = new FormData();
|
|
fetch.mockResolvedValueOnce({
|
|
ok: false,
|
|
statusText: 'Bad Request',
|
|
json: async () => ({ detail: 'Invalid speaker data' }),
|
|
});
|
|
|
|
await expect(addSpeaker(mockFormData)).rejects.toThrow('Failed to add speaker: Invalid speaker data');
|
|
expect(fetch).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|
|
|
|
describe('API Client - deleteSpeaker', () => {
|
|
beforeEach(() => {
|
|
fetch.mockClear();
|
|
});
|
|
|
|
it('should delete a speaker successfully with JSON response', async () => {
|
|
const speakerId = 'test-speaker-id-123';
|
|
const mockResponse = { message: `Speaker ${speakerId} deleted successfully` };
|
|
fetch.mockResolvedValueOnce({
|
|
ok: true,
|
|
status: 200, // Or any 2xx status that might return JSON
|
|
json: async () => mockResponse,
|
|
});
|
|
|
|
const result = await deleteSpeaker(speakerId);
|
|
expect(fetch).toHaveBeenCalledTimes(1);
|
|
expect(fetch).toHaveBeenCalledWith(`${API_BASE_URL}/speakers/${speakerId}`, {
|
|
method: 'DELETE',
|
|
});
|
|
expect(result).toEqual(mockResponse);
|
|
});
|
|
|
|
it('should handle successful deletion with 204 No Content response', async () => {
|
|
const speakerId = 'test-speaker-id-204';
|
|
fetch.mockResolvedValueOnce({
|
|
ok: true,
|
|
status: 204,
|
|
statusText: 'No Content',
|
|
// .json() is not called by the function if status is 204
|
|
});
|
|
|
|
const result = await deleteSpeaker(speakerId);
|
|
expect(fetch).toHaveBeenCalledTimes(1);
|
|
expect(fetch).toHaveBeenCalledWith(`${API_BASE_URL}/speakers/${speakerId}`, {
|
|
method: 'DELETE',
|
|
});
|
|
expect(result).toEqual({ message: `Speaker ${speakerId} deleted successfully.` });
|
|
});
|
|
|
|
it('should throw an error if deleting a speaker fails (e.g., speaker not found)', async () => {
|
|
const speakerId = 'non-existent-speaker-id';
|
|
fetch.mockResolvedValueOnce({
|
|
ok: false,
|
|
status: 404,
|
|
statusText: 'Not Found',
|
|
json: async () => ({ detail: 'Speaker not found' }),
|
|
});
|
|
|
|
await expect(deleteSpeaker(speakerId)).rejects.toThrow(`Failed to delete speaker ${speakerId}: Speaker not found`);
|
|
expect(fetch).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|
|
|
|
describe('API Client - generateDialog', () => {
|
|
beforeEach(() => {
|
|
fetch.mockClear();
|
|
});
|
|
|
|
it('should generate dialog successfully', async () => {
|
|
const mockPayload = {
|
|
output_base_name: "test_dialog",
|
|
dialog_items: [
|
|
{ type: "speech", speaker_id: "spk_1", text: "Hello.", exaggeration: 1.0, cfg_weight: 3.0, temperature: 0.5 },
|
|
{ type: "silence", duration_ms: 250 }
|
|
]
|
|
};
|
|
const mockResponse = {
|
|
log: "Dialog generated.",
|
|
concatenated_audio_url: "/audio/test_dialog_concatenated.wav",
|
|
zip_archive_url: "/audio/test_dialog.zip"
|
|
};
|
|
fetch.mockResolvedValueOnce({
|
|
ok: true,
|
|
json: async () => mockResponse,
|
|
});
|
|
|
|
const result = await generateDialog(mockPayload);
|
|
expect(fetch).toHaveBeenCalledTimes(1);
|
|
expect(fetch).toHaveBeenCalledWith(`${API_BASE_URL}/dialog/generate`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(mockPayload),
|
|
});
|
|
expect(result).toEqual(mockResponse);
|
|
});
|
|
|
|
it('should throw an error if dialog generation fails', async () => {
|
|
const mockPayload = { output_base_name: "fail_dialog", dialog_items: [] }; // Example invalid payload
|
|
fetch.mockResolvedValueOnce({
|
|
ok: false,
|
|
statusText: 'Bad Request',
|
|
json: async () => ({ detail: 'Invalid dialog data' }),
|
|
});
|
|
|
|
await expect(generateDialog(mockPayload)).rejects.toThrow('Failed to generate dialog: Invalid dialog data');
|
|
expect(fetch).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|