import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import ImagePreview from './ImagePreview';
import { ChatbotDisplayMode } from '../Chatbot';
const mockImages = [
{
fileName: 'image1.jpg',
fileSize: '2.5 MB',
image:
},
{
fileName: 'image2.png',
fileSize: '1.8 MB',
image:
},
{
fileName: 'image3.gif',
image:
}
];
const defaultProps = {
isModalOpen: true,
handleModalToggle: jest.fn(),
images: mockImages
};
describe('ImagePreview', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('renders modal when isModalOpen is true', () => {
render();
expect(screen.getByRole('dialog')).toBeInTheDocument();
});
it('does not render modal when isModalOpen is false', () => {
render();
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
});
it('displays custom title when provided', () => {
const customTitle = 'Custom image preview';
render();
expect(screen.getByRole('heading', { name: customTitle })).toBeInTheDocument();
});
it('displays default title when no title provided', () => {
render();
expect(screen.getByRole('heading', { name: /Preview images/i })).toBeInTheDocument();
});
it('calls handleModalToggle when modal is closed', () => {
const mockHandleToggle = jest.fn();
render();
const closeButton = screen.getByRole('button', { name: /close/i });
fireEvent.click(closeButton);
expect(mockHandleToggle).toHaveBeenCalledTimes(1);
});
it('displays first image by default', () => {
render();
expect(screen.getByText('image1.jpg')).toBeInTheDocument();
expect(screen.getByText('2.5 MB')).toBeInTheDocument();
expect(screen.getByAltText('Test image 1')).toBeInTheDocument();
});
it('displays page counter correctly', () => {
render();
expect(screen.getByText('1/3')).toBeInTheDocument();
});
it('navigates to next image when next button is clicked', () => {
const mockOnNextClick = jest.fn();
render();
const nextButton = screen.getByRole('button', { name: /Go to next image/i });
fireEvent.click(nextButton);
expect(mockOnNextClick).toHaveBeenCalled();
expect(screen.getByText('2/3')).toBeInTheDocument();
expect(screen.getByText('image2.png')).toBeInTheDocument();
});
it('navigates to previous image when previous button is clicked', () => {
const mockOnPreviousClick = jest.fn();
render();
// First go to page 2
const nextButton = screen.getByRole('button', { name: /Go to next image/i });
fireEvent.click(nextButton);
// Then go back to page 1
const previousButton = screen.getByRole('button', { name: /Go to previous image/i });
fireEvent.click(previousButton);
expect(mockOnPreviousClick).toHaveBeenCalled();
expect(screen.getByText('1/3')).toBeInTheDocument();
});
it('calls onSetPage when page changes', () => {
const mockOnSetPage = jest.fn();
render();
const nextButton = screen.getByRole('button', { name: /Go to next image/i });
fireEvent.click(nextButton);
expect(mockOnSetPage).toHaveBeenCalledWith(expect.any(Object), 2);
});
it('disables previous button on first page', () => {
render();
const previousButton = screen.getByRole('button', { name: /Go to previous image/i });
expect(previousButton).toBeDisabled();
});
it('disables next button on last page', () => {
render();
// Navigate to last page
const nextButton = screen.getByRole('button', { name: /Go to next image/i });
fireEvent.click(nextButton); // page 2
fireEvent.click(nextButton); // page 3
expect(nextButton).toBeDisabled();
});
it('disables both navigation buttons when isDisabled is true', () => {
render();
const previousButton = screen.getByRole('button', { name: /Go to previous image/i });
const nextButton = screen.getByRole('button', { name: /Go to next image/i });
expect(previousButton).toBeDisabled();
expect(nextButton).toBeDisabled();
});
it('uses custom aria labels for pagination', () => {
const customLabels = {
paginationAriaLabel: 'Custom pagination',
toPreviousPageAriaLabel: 'Go to previous image',
toNextPageAriaLabel: 'Go to next image'
};
render();
expect(screen.getByRole('navigation', { name: 'Custom pagination' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Go to previous image' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Go to next image' })).toBeInTheDocument();
});
it('renders with compact mode when isCompact is true', () => {
render();
const modal = screen.getByRole('dialog');
expect(modal).toHaveClass('pf-m-compact');
});
it('applies custom className when provided', () => {
const customClassName = 'custom-image-preview';
render();
const modal = screen.getByRole('dialog');
expect(modal).toHaveClass(customClassName);
});
it('applies display mode class correctly', () => {
render();
const modal = screen.getByRole('dialog');
expect(modal).toHaveClass('pf-chatbot__image-preview-modal--embedded');
});
it('passes additional props to ChatbotModal', () => {
const modalClass = 'custom-modal-class';
const additionalProps = {
'data-testid': 'modal',
className: modalClass
};
render();
const modal = screen.getByTestId('modal');
expect(modal).toBeInTheDocument();
expect(modal).toBeInTheDocument();
expect(modal).toHaveClass(modalClass);
});
it('passes modalHeaderProps correctly', () => {
const headerClass = 'custom-modal-header-class';
const headerProps = {
'data-testid': 'header',
className: headerClass
};
render();
expect(screen.getByTestId('header')).toBeInTheDocument();
expect(screen.getByTestId('header')).toHaveClass(headerClass);
});
it('passes modalBodyProps correctly', () => {
const bodyClass = 'custom-modal-body-class';
const bodyProps = {
'data-testid': 'body',
className: bodyClass
};
render();
expect(screen.getByTestId('body')).toBeInTheDocument();
expect(screen.getByTestId('body')).toHaveClass(bodyClass);
});
it('handles single image without pagination', () => {
const singleImage = [mockImages[0]];
render();
expect(screen.queryByText('1/1')).not.toBeInTheDocument();
expect(screen.queryByRole('button', { name: /Go to previous image/i })).not.toBeInTheDocument();
expect(screen.queryByRole('button', { name: /Go to next image/i })).not.toBeInTheDocument();
});
it('calls onCloseFileDetailsLabel when file details close button is clicked', () => {
const mockOnClose = jest.fn();
render();
const closeButton = screen.getByRole('button', { name: /Close image1.jpg/i });
fireEvent.click(closeButton);
expect(mockOnClose).toHaveBeenCalled();
});
it('passes fileDetailsLabelProps correctly to FileDetailsLabel', () => {
const customFileDetailsProps = {
'data-testid': 'custom-file-details'
};
render();
expect(screen.getByTestId('custom-file-details')).toBeInTheDocument();
});
it('displays file details for current page when navigating', () => {
render();
// Initially shows first image details
expect(screen.getByText('image1.jpg')).toBeInTheDocument();
expect(screen.getByText('2.5 MB')).toBeInTheDocument();
// Navigate to second page
const nextButton = screen.getByRole('button', { name: /Go to next image/i });
fireEvent.click(nextButton);
// Should now show second image details
expect(screen.getByText('image2.png')).toBeInTheDocument();
expect(screen.getByText('1.8 MB')).toBeInTheDocument();
// Navigate to third page
fireEvent.click(nextButton);
// Should now show third image details (no file size)
expect(screen.getByText('image3.gif')).toBeInTheDocument();
expect(screen.queryByText(/MB/)).not.toBeInTheDocument();
});
it('sets hasTruncation to false on FileDetailsLabel', () => {
const longFileName = 'very-long-filename-that-would-normally-be-truncated-in-other-contexts.jpg';
const imageWithLongName = {
fileName: longFileName,
fileSize: '1.0 MB',
image:
};
render();
expect(screen.getByText(longFileName)).toBeInTheDocument();
});
});