Files
ReadMeABook/tests/components/bookdate/RecommendationCard.test.tsx
T
kikootwo a97979358f Implement file hash-based library matching and remove fuzzy ASIN matching
Adds file hash-based matching for Audiobookshelf library items to ensure 100% accurate ASIN assignment for RMAB-organized content. Removes fuzzy matching from library availability checks, making all matching ASIN-only to eliminate false positives and race conditions. Updates database schema, processors, and matcher utilities; adds new tests and documentation for the new matching strategy. Removes obsolete scripts, Dockerfile, and related tests; updates docker-compose for test environments.
2026-01-28 11:42:00 -05:00

140 lines
4.6 KiB
TypeScript

/**
* Component: BookDate Recommendation Card Tests
* Documentation: documentation/features/bookdate.md
*/
// @vitest-environment jsdom
import React from 'react';
import { act, fireEvent, render, screen } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
const swipeHandlers: {
onSwipeStart?: () => void;
onSwiping?: (eventData: { deltaX: number; deltaY: number }) => void;
onSwiped?: (eventData: { deltaX: number; deltaY: number }) => void;
} = {};
vi.mock('react-swipeable', () => ({
useSwipeable: (handlers: any) => {
swipeHandlers.onSwipeStart = handlers.onSwipeStart;
swipeHandlers.onSwiping = handlers.onSwiping;
swipeHandlers.onSwiped = handlers.onSwiped;
return {};
},
}));
const recommendation = {
title: 'Sample Book',
author: 'Sample Author',
narrator: 'Sample Narrator',
rating: 4.5,
description: 'A sample description',
aiReason: 'Because it matches your tastes.',
};
describe('RecommendationCard', () => {
beforeEach(() => {
swipeHandlers.onSwipeStart = undefined;
swipeHandlers.onSwiping = undefined;
swipeHandlers.onSwiped = undefined;
});
it('shows the request toast and triggers a request action', async () => {
const onSwipe = vi.fn();
const { RecommendationCard } = await import('@/components/bookdate/RecommendationCard');
render(<RecommendationCard recommendation={recommendation} onSwipe={onSwipe} />);
const requestButtons = screen.getAllByRole('button', { name: /Request/ });
fireEvent.click(requestButtons[0]);
expect(screen.getByText('Request "Sample Book"?')).toBeInTheDocument();
const toastRequestButtons = screen.getAllByRole('button', { name: /Request/ });
fireEvent.click(toastRequestButtons[toastRequestButtons.length - 1]);
expect(onSwipe).toHaveBeenCalledWith('right', false);
});
it('marks a recommendation as liked from the toast', async () => {
const onSwipe = vi.fn();
const { RecommendationCard } = await import('@/components/bookdate/RecommendationCard');
render(<RecommendationCard recommendation={recommendation} onSwipe={onSwipe} />);
const requestButtons = screen.getAllByRole('button', { name: /Request/ });
fireEvent.click(requestButtons[0]);
fireEvent.click(screen.getByRole('button', { name: 'Mark as Liked' }));
expect(onSwipe).toHaveBeenCalledWith('right', true);
});
it('triggers dislike and dismiss actions from desktop buttons', async () => {
const onSwipe = vi.fn();
const { RecommendationCard } = await import('@/components/bookdate/RecommendationCard');
render(<RecommendationCard recommendation={recommendation} onSwipe={onSwipe} />);
fireEvent.click(screen.getByRole('button', { name: /Not Interested/ }));
fireEvent.click(screen.getByRole('button', { name: /Dismiss/ }));
expect(onSwipe).toHaveBeenCalledWith('left');
expect(onSwipe).toHaveBeenCalledWith('up');
});
it('shows drag overlays based on swipe direction', async () => {
const onSwipe = vi.fn();
const { RecommendationCard } = await import('@/components/bookdate/RecommendationCard');
render(<RecommendationCard recommendation={recommendation} onSwipe={onSwipe} />);
act(() => {
swipeHandlers.onSwipeStart?.();
swipeHandlers.onSwiping?.({ deltaX: -80, deltaY: 0 });
});
expect(screen.getByText('Dislike')).toBeInTheDocument();
});
it('triggers an upward swipe from gesture handling', async () => {
const onSwipe = vi.fn();
const { RecommendationCard } = await import('@/components/bookdate/RecommendationCard');
render(<RecommendationCard recommendation={recommendation} onSwipe={onSwipe} />);
act(() => {
swipeHandlers.onSwiped?.({ deltaX: 0, deltaY: -150 });
});
expect(onSwipe).toHaveBeenCalledWith('up');
});
it('ignores swipe gestures when not draggable', async () => {
const onSwipe = vi.fn();
const { RecommendationCard } = await import('@/components/bookdate/RecommendationCard');
render(
<RecommendationCard recommendation={recommendation} onSwipe={onSwipe} isDraggable={false} />
);
act(() => {
swipeHandlers.onSwiped?.({ deltaX: 150, deltaY: 0 });
});
expect(onSwipe).not.toHaveBeenCalled();
expect(screen.queryByText(/Request "Sample Book"/)).toBeNull();
});
it('hides desktop actions when not the top card', async () => {
const onSwipe = vi.fn();
const { RecommendationCard } = await import('@/components/bookdate/RecommendationCard');
render(
<RecommendationCard recommendation={recommendation} onSwipe={onSwipe} stackPosition={1} />
);
expect(screen.queryByRole('button', { name: /Not Interested/ })).toBeNull();
});
});