mirror of
https://github.com/kikootwo/ReadMeABook.git
synced 2026-06-02 20:30:10 +00:00
a97979358f
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.
140 lines
4.6 KiB
TypeScript
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();
|
|
});
|
|
});
|