Files
ReadMeABook/tests/components/requests/InteractiveTorrentSearchModal.test.tsx
T
kikootwo eca24e46a8 Add test mocks and update delete API assertion
Add missing mocks used by updated code paths: mock PreferencesContext in profile page tests and add useReplaceWithTorrent/replaceWithTorrent mock for InteractiveTorrentSearchModal tests. Update Audiobookshelf API test to expect DELETE to include ?hard=1 and Authorization header. Extend the prisma test helper in audiobook-matcher tests with a reportedIssue.findMany mock and ensure it resolves to an empty array for the test.
2026-02-11 17:02:21 -05:00

176 lines
5.3 KiB
TypeScript

/**
* Component: Interactive Torrent Search Modal Tests
* Documentation: documentation/frontend/components.md
*/
// @vitest-environment jsdom
import React from 'react';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
const searchByRequestMock = vi.hoisted(() => vi.fn());
const selectTorrentMock = vi.hoisted(() => vi.fn());
const searchByAudiobookMock = vi.hoisted(() => vi.fn());
const requestWithTorrentMock = vi.hoisted(() => vi.fn());
const searchEbooksMock = vi.hoisted(() => vi.fn());
const selectEbookMock = vi.hoisted(() => vi.fn());
const searchEbooksByAsinMock = vi.hoisted(() => vi.fn());
const selectEbookByAsinMock = vi.hoisted(() => vi.fn());
const replaceWithTorrentMock = vi.hoisted(() => vi.fn());
vi.mock('@/lib/hooks/useReportedIssues', () => ({
useReplaceWithTorrent: () => ({
replaceWithTorrent: replaceWithTorrentMock,
isLoading: false,
error: null,
}),
}));
vi.mock('@/lib/hooks/useRequests', () => ({
useInteractiveSearch: () => ({
searchTorrents: searchByRequestMock,
isLoading: false,
error: null,
}),
useSelectTorrent: () => ({
selectTorrent: selectTorrentMock,
isLoading: false,
error: null,
}),
useSearchTorrents: () => ({
searchTorrents: searchByAudiobookMock,
isLoading: false,
error: null,
}),
useRequestWithTorrent: () => ({
requestWithTorrent: requestWithTorrentMock,
isLoading: false,
error: null,
}),
useInteractiveSearchEbook: () => ({
searchEbooks: searchEbooksMock,
isLoading: false,
error: null,
}),
useSelectEbook: () => ({
selectEbook: selectEbookMock,
isLoading: false,
error: null,
}),
useInteractiveSearchEbookByAsin: () => ({
searchEbooks: searchEbooksByAsinMock,
isLoading: false,
error: null,
}),
useSelectEbookByAsin: () => ({
selectEbook: selectEbookByAsinMock,
isLoading: false,
error: null,
}),
}));
const baseResult = {
guid: 'torrent-1',
rank: 1,
title: 'Test Torrent',
size: 2.4 * 1024 ** 3,
score: 88,
bonusPoints: 5,
seeders: 42,
indexer: 'ProIndexer',
format: 'M4B',
infoUrl: 'https://example.com/torrent',
};
describe('InteractiveTorrentSearchModal', () => {
it('searches by request id on open and confirms download', async () => {
searchByRequestMock.mockResolvedValueOnce([baseResult]);
selectTorrentMock.mockResolvedValueOnce(undefined);
const onClose = vi.fn();
const onSuccess = vi.fn();
const { InteractiveTorrentSearchModal } = await import('@/components/requests/InteractiveTorrentSearchModal');
render(
<InteractiveTorrentSearchModal
isOpen={true}
onClose={onClose}
onSuccess={onSuccess}
requestId="req-123"
audiobook={{ title: 'Test Book', author: 'Test Author' }}
/>
);
await waitFor(() => {
expect(searchByRequestMock).toHaveBeenCalledWith('req-123', undefined);
});
expect(await screen.findByText('Test Torrent')).toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: 'Get' }));
fireEvent.click(await screen.findByRole('button', { name: 'Download' }));
await waitFor(() => {
expect(selectTorrentMock).toHaveBeenCalledWith('req-123', baseResult);
});
expect(onSuccess).toHaveBeenCalled();
expect(onClose).toHaveBeenCalled();
});
it('searches by audiobook data and requests with torrent', async () => {
searchByAudiobookMock.mockResolvedValueOnce([baseResult]);
requestWithTorrentMock.mockResolvedValueOnce(undefined);
const onClose = vi.fn();
const fullAudiobook = { asin: 'ASIN-1', title: 'Test Book', author: 'Test Author' };
const { InteractiveTorrentSearchModal } = await import('@/components/requests/InteractiveTorrentSearchModal');
render(
<InteractiveTorrentSearchModal
isOpen={true}
onClose={onClose}
audiobook={{ title: 'Test Book', author: 'Test Author' }}
fullAudiobook={fullAudiobook as any}
/>
);
await waitFor(() => {
expect(searchByAudiobookMock).toHaveBeenCalledWith('Test Book', 'Test Author', 'ASIN-1');
});
fireEvent.click(screen.getByRole('button', { name: 'Get' }));
fireEvent.click(await screen.findByRole('button', { name: 'Download' }));
await waitFor(() => {
expect(requestWithTorrentMock).toHaveBeenCalledWith(fullAudiobook, baseResult);
});
expect(onClose).toHaveBeenCalled();
});
it('uses a custom title when pressing Enter', async () => {
searchByRequestMock.mockResolvedValueOnce([]);
searchByRequestMock.mockResolvedValueOnce([]);
const { InteractiveTorrentSearchModal } = await import('@/components/requests/InteractiveTorrentSearchModal');
render(
<InteractiveTorrentSearchModal
isOpen={true}
onClose={vi.fn()}
requestId="req-456"
audiobook={{ title: 'Original Title', author: 'Author' }}
/>
);
await waitFor(() => {
expect(searchByRequestMock).toHaveBeenCalledWith('req-456', undefined);
});
const input = screen.getByPlaceholderText('Search title...');
fireEvent.change(input, { target: { value: 'Custom Title' } });
fireEvent.keyDown(input, { key: 'Enter', code: 'Enter' });
await waitFor(() => {
expect(searchByRequestMock).toHaveBeenNthCalledWith(2, 'req-456', 'Custom Title');
});
});
});