mirror of
https://github.com/kikootwo/ReadMeABook.git
synced 2026-06-02 20:30:10 +00:00
Add modal props & update RequestCard/tests
Extend AudiobookDetailsModal props with onStatusChange, onIgnoreChange, hideRequestActions, hasReportedIssue, and aiReason. Stop forcing hideRequestActions when opening the modal from RequestCard so the modal can control whether request actions are shown. Add tests: verify admin sticky footer/status pill in AudiobookDetailsModal for pending requests, and add a RequestCard test that mocks AudiobookDetailsModal to assert the modal receives isOpen, asin and that hideRequestActions is not forced. Reset the new mock between tests.
This commit is contained in:
@@ -109,10 +109,15 @@ interface AudiobookDetailsModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onRequestSuccess?: () => void;
|
||||
onStatusChange?: (newStatus: string) => void;
|
||||
onIgnoreChange?: (isIgnored: boolean) => void;
|
||||
isRequested?: boolean;
|
||||
requestStatus?: string | null;
|
||||
isAvailable?: boolean;
|
||||
requestedByUsername?: string | null;
|
||||
hideRequestActions?: boolean; // Hides sticky action bar for read-only contexts (BookDate, ShelvesSection)
|
||||
hasReportedIssue?: boolean;
|
||||
aiReason?: string | null;
|
||||
adminActions?: React.ReactNode; // Optional admin buttons (Approve/Search/Deny) rendered as second row in action bar
|
||||
}
|
||||
|
||||
|
||||
@@ -273,7 +273,6 @@ export function RequestCard({ request, showActions = true }: RequestCardProps) {
|
||||
onClose={() => setShowDetailsModal(false)}
|
||||
requestStatus={request.status}
|
||||
isAvailable={COMPLETED_STATUSES.includes(request.status as typeof COMPLETED_STATUSES[number])}
|
||||
hideRequestActions
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@@ -305,4 +305,26 @@ describe('AudiobookDetailsModal', () => {
|
||||
|
||||
expect(screen.queryByText('Request failed')).toBeNull();
|
||||
});
|
||||
|
||||
it('renders sticky footer with status pill and admin icons when opened from a pending request', async () => {
|
||||
useAuthMock.mockReturnValue({ user: { id: 'admin-1', username: 'admin', role: 'admin' } });
|
||||
const { AudiobookDetailsModal } = await import('@/components/audiobooks/AudiobookDetailsModal');
|
||||
|
||||
render(
|
||||
<AudiobookDetailsModal
|
||||
asin="ASIN123"
|
||||
isOpen={true}
|
||||
onClose={vi.fn()}
|
||||
requestStatus="pending"
|
||||
isAvailable={false}
|
||||
/>
|
||||
);
|
||||
|
||||
await act(async () => {});
|
||||
|
||||
const statusPill = screen.getByRole('button', { name: 'Requested' });
|
||||
expect(statusPill).toBeDisabled();
|
||||
expect(screen.getByTitle('Interactive Search')).toBeInTheDocument();
|
||||
expect(screen.getByTitle('Manual Import')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,11 +10,19 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
const cancelRequestMock = vi.hoisted(() => vi.fn());
|
||||
const detailsModalSpy = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock('@/lib/hooks/useRequests', () => ({
|
||||
useCancelRequest: () => ({ cancelRequest: cancelRequestMock, isLoading: false }),
|
||||
}));
|
||||
|
||||
vi.mock('@/components/audiobooks/AudiobookDetailsModal', () => ({
|
||||
AudiobookDetailsModal: (props: any) => {
|
||||
detailsModalSpy(props);
|
||||
return <div data-testid="audiobook-details-modal" data-open={String(props.isOpen)} />;
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('next/image', () => ({
|
||||
__esModule: true,
|
||||
default: (props: any) => <img {...props} />,
|
||||
@@ -52,6 +60,7 @@ const baseRequest = {
|
||||
describe('RequestCard', () => {
|
||||
beforeEach(() => {
|
||||
cancelRequestMock.mockReset();
|
||||
detailsModalSpy.mockReset();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -218,4 +227,25 @@ describe('RequestCard', () => {
|
||||
|
||||
expect(screen.queryByText(/^Releases /)).toBeNull();
|
||||
});
|
||||
|
||||
it('opens AudiobookDetailsModal without hiding request actions when details are viewed', async () => {
|
||||
const { RequestCard } = await import('@/components/requests/RequestCard');
|
||||
|
||||
render(
|
||||
<RequestCard
|
||||
request={{
|
||||
...baseRequest,
|
||||
audiobook: { ...baseRequest.audiobook, audibleAsin: 'ASIN123' },
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: baseRequest.audiobook.title }));
|
||||
|
||||
expect(detailsModalSpy).toHaveBeenCalled();
|
||||
const props = detailsModalSpy.mock.calls.at(-1)?.[0];
|
||||
expect(props.isOpen).toBe(true);
|
||||
expect(props.asin).toBe('ASIN123');
|
||||
expect(props.hideRequestActions).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user