import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { TabletDesktopPlayer } from '../TabletDesktopPlayer';
// Mocking styled-components
jest.mock('../styles', () => ({
TabletDesktopWrapper: ({ children }: any) => (
{children}
),
TabletDesktopInnerWrapper: ({ children }: any) => (
{children}
),
LeftControls: ({ children }: any) => (
{children}
),
CenterControls: ({ children }: any) => (
{children}
),
RightControls: ({ children }: any) => (
{children}
),
TabletDesktopPlayPauseButton: ({ children, ...props }: any) => (
),
TabletDesktopStatusText: ({ children }: any) => (
{children}
),
TabletDesktopVolumeControlContainer: ({ children }: any) => (
{children}
),
TabletDesktopVolumeButton: ({ children, ...props }: any) => (
),
TabletDesktopVolumeSlider: ({ ...props }: any) => (
),
TabletDesktopTimeDisplay: ({ children, testId }: any) => (
{children}
),
TabletDesktopSeekBar: ({ ...props }: any) => (
),
TabletDesktopSpeedButton: ({ children, ...props }: any) => (
),
SpeedSelectModal: ({ children }: any) => (
{children}
),
SpeedOptionItem: ({ children, ...props }: any) => (
{children}
),
TabletDesktopCloseButton: ({ children, ...props }: any) => (
),
SpeedButtonContainer: ({ children }: any) => (
{children}
),
SpeedOptionsContainer: ({ children }: any) => (
{children}
)
}));
// Mocking icons
jest.mock('@times-components/icons', () => ({
PlayIcon: () => ,
PauseIcon: () => ,
IconVolume: () => ,
IconCheck: () => ,
AudioCloseIcon: () =>
}));
// Mocking utils
jest.mock('../utils', () => ({
formatTime: (seconds: number) => {
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins}:${secs < 10 ? '0' : ''}${secs}`;
}
}));
describe('TabletDesktopPlayer', () => {
const mockTogglePlayPause = jest.fn();
const mockHandleSeek = jest.fn();
const mockHandleVolumeChange = jest.fn();
const mockHandleSpeedSelect = jest.fn();
const mockSetIsSpeedModalOpen = jest.fn();
const mockSetIsVolumeSliderVisible = jest.fn();
const mockOnClose = jest.fn();
const mockSetVolume = jest.fn();
const mockHandleSpeedChange = jest.fn();
afterEach(() => {
jest.clearAllMocks();
});
const defaultProps = {
isPlaying: false,
togglePlayPause: mockTogglePlayPause,
currentTime: 30,
duration: 120,
allowTogglePlay: true,
allowSeek: true,
allowVolumeChange: true,
volume: 0.5,
handleSeek: mockHandleSeek,
handleVolumeChange: mockHandleVolumeChange,
speed: 1.0,
allowPlaybackRateChange: true,
isSpeedModalOpen: false,
setIsSpeedModalOpen: mockSetIsSpeedModalOpen,
speedOptions: [0.5, 1.0, 1.5, 2.0],
handleSpeedSelect: mockHandleSpeedSelect,
isVolumeSliderVisible: false,
setIsVolumeSliderVisible: mockSetIsVolumeSliderVisible,
onClose: mockOnClose,
allowExpandCollapse: true,
isMobile: false,
audioRef: React.createRef(),
setVolume: mockSetVolume,
handleSpeedChange: mockHandleSpeedChange
};
const renderComponent = (props = {}) =>
render();
test('renders correctly with all controls enabled and not playing', () => {
const { getByTestId, getAllByTestId, queryByTestId } = renderComponent();
// Wrapper and Inner Wrapper
expect(getByTestId('tablet-desktop-wrapper')).toBeInTheDocument();
expect(getByTestId('tablet-desktop-inner-wrapper')).toBeInTheDocument();
// Left Controls
expect(getByTestId('left-controls')).toBeInTheDocument();
expect(getByTestId('play-pause-button')).toBeInTheDocument();
expect(getByTestId('status-text')).toHaveTextContent('Paused');
expect(getByTestId('volume-control-container')).toBeInTheDocument();
expect(getByTestId('volume-button')).toBeInTheDocument();
expect(queryByTestId('volume-slider')).not.toBeInTheDocument();
// Center Controls
expect(getByTestId('center-controls')).toBeInTheDocument();
const timeDisplays = getAllByTestId('time-display');
expect(timeDisplays[0].textContent).toBe('0:30');
expect(getByTestId('seek-bar')).toBeInTheDocument();
expect(getByTestId('seek-bar')).toHaveAttribute('min', '0');
expect(getByTestId('seek-bar')).toHaveAttribute('max', '120');
expect(getByTestId('seek-bar')).toHaveAttribute('value', '30');
expect(getByTestId('seek-bar')).not.toBeDisabled();
expect(getByTestId('seek-bar')).toHaveAttribute('aria-label', 'Seek Bar');
expect(timeDisplays[1].textContent).toBe('2:00');
// Right Controls
expect(getByTestId('right-controls')).toBeInTheDocument();
expect(getByTestId('speed-button-container')).toBeInTheDocument();
expect(getByTestId('speed-button')).toBeInTheDocument();
expect(queryByTestId('speed-select-modal')).not.toBeInTheDocument();
expect(getByTestId('close-button')).toBeInTheDocument();
});
test('renders PlayIcon when not playing and PauseIcon when playing', () => {
const { getByTestId, rerender, queryByTestId } = renderComponent({
isPlaying: false
});
expect(getByTestId('play-icon')).toBeInTheDocument();
expect(queryByTestId('pause-icon')).not.toBeInTheDocument();
rerender();
expect(getByTestId('pause-icon')).toBeInTheDocument();
expect(queryByTestId('play-icon')).not.toBeInTheDocument();
});
test('calls togglePlayPause when Play/Pause button is clicked', () => {
const { getByLabelText } = renderComponent();
const playPauseButton = getByLabelText('Play');
fireEvent.click(playPauseButton);
expect(mockTogglePlayPause).toHaveBeenCalledTimes(1);
});
test('calls handleSeek when Seek Bar is moved', () => {
const { getByTestId } = renderComponent();
const seekBar = getByTestId('seek-bar') as HTMLInputElement;
fireEvent.change(seekBar, { target: { value: '60' } });
expect(mockHandleSeek).toHaveBeenCalledTimes(1);
expect(mockHandleSeek).toHaveBeenCalledWith(60);
});
test('calls handleVolumeChange when volume slider is adjusted', () => {
const { getByTestId } = render(
);
const volumeSlider = getByTestId('volume-slider') as HTMLInputElement;
fireEvent.change(volumeSlider, { target: { value: '0.7' } });
expect(mockHandleVolumeChange).toHaveBeenCalledTimes(1);
expect(mockHandleVolumeChange).toHaveBeenCalledWith(0.7);
});
test('opens speed modal when Speed button is clicked', () => {
const { getByTestId } = renderComponent();
const speedButton = getByTestId('speed-button');
fireEvent.click(speedButton);
expect(mockSetIsSpeedModalOpen).toHaveBeenCalledWith(true);
});
test('does not render speed button when allowPlaybackRateChange is false', () => {
const { queryByTestId } = render(
);
expect(queryByTestId('speed-button-container')).not.toBeInTheDocument();
});
test('renders speed modal when isSpeedModalOpen is true', () => {
const { getByTestId } = render(
);
expect(getByTestId('speed-select-modal')).toBeInTheDocument();
});
test('calls onClose when Close button is clicked', () => {
const { getByLabelText } = renderComponent();
const closeButton = getByLabelText('Close Player');
fireEvent.click(closeButton);
expect(mockOnClose).toHaveBeenCalledTimes(1);
});
test('does not render volume controls when allowVolumeChange is false', () => {
const { queryByTestId } = render(
);
expect(queryByTestId('volume-control-container')).not.toBeInTheDocument();
});
test('does not render speed controls when allowPlaybackRateChange is false', () => {
const { queryByTestId } = render(
);
expect(queryByTestId('speed-button-container')).not.toBeInTheDocument();
});
test('does not render Close button when allowExpandCollapse is false', () => {
const { queryByTestId } = render(
);
expect(queryByTestId('close-button')).not.toBeInTheDocument();
});
test('correctly displays formatted currentTime and duration', () => {
const { getAllByTestId } = renderComponent();
const timeDisplays = getAllByTestId('time-display');
expect(timeDisplays[0].textContent).toBe('0:30');
expect(timeDisplays[1].textContent).toBe('2:00');
});
test('sets correct attributes on Seek Bar based on props', () => {
const { getByTestId } = renderComponent();
const seekBar = getByTestId('seek-bar');
expect(seekBar).toHaveAttribute('type', 'range');
expect(seekBar).toHaveAttribute('min', '0');
expect(seekBar).toHaveAttribute('max', '120');
expect(seekBar).toHaveAttribute('value', '30');
expect(seekBar).not.toBeDisabled();
expect(seekBar).toHaveAttribute('aria-label', 'Seek Bar');
expect(seekBar).toHaveAttribute('progress', '25');
});
test('handles duration of zero gracefully', () => {
const { getByTestId } = render(
);
const seekBar = getByTestId('seek-bar');
expect(seekBar).toHaveAttribute('max', '0');
expect(seekBar).toHaveAttribute('value', '0');
expect(seekBar).toHaveAttribute('progress', '0');
});
});