react-h5-audio-player
Version:
A customizable React audio player. Written in TypeScript. Mobile compatible. Keyboard friendly
746 lines (618 loc) • 28.3 kB
JavaScript
import React from 'react'
import { render, fireEvent, act, screen, waitFor } from '@testing-library/react'
import '@testing-library/jest-dom'
import H5AudioPlayer, { RHAP_UI } from './index'
// Mock Icon component from iconify
jest.mock('@iconify/react', () => ({
Icon: ({ icon }) => <span data-testid={`icon-${icon}`}>{icon}</span>
}))
describe('H5AudioPlayer', () => {
// Helper function to setup the audio element after component renders
const setupAudioElement = (container, options = {}) => {
const audioElement = container.querySelector('audio')
if (audioElement) {
// Mock audio properties and methods
const mockMethods = {
play: jest.fn(() => Promise.resolve()),
pause: jest.fn(),
load: jest.fn(),
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
}
const mockProperties = {
currentTime: 0,
duration: 100,
volume: 1,
loop: false,
muted: false,
ended: false,
error: null,
src: '',
readyState: 4,
buffered: { length: 0, start: () => 0, end: () => 0 },
HAVE_NOTHING: 0,
HAVE_METADATA: 1,
HAVE_CURRENT_DATA: 2,
HAVE_FUTURE_DATA: 3,
HAVE_ENOUGH_DATA: 4,
...options
}
// Only assign methods and writable properties
Object.assign(audioElement, mockMethods)
// Mock read-only properties using defineProperty
Object.defineProperty(audioElement, 'paused', {
value: options.paused !== undefined ? options.paused : true,
writable: true,
configurable: true
})
Object.defineProperty(audioElement, 'currentTime', {
value: options.currentTime || 0,
writable: true,
configurable: true
})
Object.defineProperty(audioElement, 'duration', {
value: options.duration || 100,
writable: true,
configurable: true
})
return audioElement
}
return null
}
describe('Basic Rendering', () => {
it('renders audio player with default props', () => {
const { container } = render(<H5AudioPlayer />)
expect(container.querySelector('.rhap_container')).toBeInTheDocument()
expect(container.querySelector('audio')).toBeInTheDocument()
})
it('renders with custom className', () => {
const { container } = render(<H5AudioPlayer className="custom-class" />)
expect(container.querySelector('.rhap_container')).toHaveClass('custom-class')
})
it('renders with custom style', () => {
const customStyle = { backgroundColor: 'red' }
const { container } = render(<H5AudioPlayer style={customStyle} />)
expect(container.querySelector('.rhap_container')).toHaveStyle('background-color: red')
})
it('renders with header and footer', () => {
const header = <div data-testid="header">Header Content</div>
const footer = <div data-testid="footer">Footer Content</div>
render(<H5AudioPlayer header={header} footer={footer} />)
expect(screen.getByTestId('header')).toBeInTheDocument()
expect(screen.getByTestId('footer')).toBeInTheDocument()
})
})
describe('Audio Properties', () => {
it('renders audio element with correct src', () => {
const src = 'test-audio.mp3'
const { container } = render(<H5AudioPlayer src={src} />)
expect(container.querySelector('audio')).toHaveAttribute('src', src)
})
it('renders audio element with autoPlay', () => {
const { container } = render(<H5AudioPlayer autoPlay />)
expect(container.querySelector('audio')).toHaveAttribute('autoplay')
})
it('renders audio element with loop', () => {
const { container } = render(<H5AudioPlayer loop />)
expect(container.querySelector('audio')).toHaveAttribute('loop')
})
it('renders audio element with preload attribute', () => {
const { container } = render(<H5AudioPlayer preload="metadata" />)
expect(container.querySelector('audio')).toHaveAttribute('preload', 'metadata')
})
it('renders audio element with crossOrigin', () => {
const { container } = render(<H5AudioPlayer crossOrigin="anonymous" />)
expect(container.querySelector('audio')).toHaveAttribute('crossorigin', 'anonymous')
})
it('renders audio element with mediaGroup', () => {
const { container } = render(<H5AudioPlayer mediaGroup="test-group" />)
expect(container.querySelector('audio')).toHaveAttribute('mediagroup', 'test-group')
})
})
describe('Layout Options', () => {
it('renders with stacked layout by default', () => {
const { container } = render(<H5AudioPlayer />)
expect(container.querySelector('.rhap_main')).toHaveClass('rhap_stacked')
})
it('renders with horizontal layout', () => {
const { container } = render(<H5AudioPlayer layout="horizontal" />)
expect(container.querySelector('.rhap_main')).toHaveClass('rhap_horizontal')
})
it('renders with stacked-reverse layout', () => {
const { container } = render(<H5AudioPlayer layout="stacked-reverse" />)
expect(container.querySelector('.rhap_main')).toHaveClass('rhap_stacked-reverse')
})
it('renders with horizontal-reverse layout', () => {
const { container } = render(<H5AudioPlayer layout="horizontal-reverse" />)
expect(container.querySelector('.rhap_main')).toHaveClass('rhap_horizontal-reverse')
})
})
describe('UI Components', () => {
it('renders default progress bar section', () => {
const { container } = render(<H5AudioPlayer />)
expect(container.querySelector('#rhap_current-time')).toBeInTheDocument()
expect(container.querySelector('.rhap_progress-container')).toBeInTheDocument()
expect(container.querySelector('.rhap_total-time')).toBeInTheDocument()
})
it('renders custom progress bar section', () => {
const customProgressBarSection = [RHAP_UI.CURRENT_LEFT_TIME, RHAP_UI.PROGRESS_BAR]
const { container } = render(
<H5AudioPlayer customProgressBarSection={customProgressBarSection} />
)
expect(container.querySelector('#rhap_current-left-time')).toBeInTheDocument()
expect(container.querySelector('.rhap_progress-container')).toBeInTheDocument()
expect(container.querySelector('.rhap_total-time')).not.toBeInTheDocument()
})
it('renders main controls', () => {
const { container } = render(<H5AudioPlayer />)
expect(container.querySelector('.rhap_main-controls')).toBeInTheDocument()
})
it('renders volume controls', () => {
const { container } = render(<H5AudioPlayer />)
expect(container.querySelector('.rhap_volume-controls')).toBeInTheDocument()
})
it('renders additional controls with loop button by default', () => {
const { container } = render(<H5AudioPlayer />)
expect(container.querySelector('.rhap_additional-controls')).toBeInTheDocument()
expect(container.querySelector('.rhap_repeat-button')).toBeInTheDocument()
})
it('renders skip controls when enabled', () => {
const { container } = render(
<H5AudioPlayer
showSkipControls
onClickPrevious={() => {}}
onClickNext={() => {}}
/>
)
const mainControls = container.querySelector('.rhap_main-controls')
expect(mainControls).toBeInTheDocument()
})
it('renders jump controls when enabled (default)', () => {
const { container } = render(<H5AudioPlayer />)
const mainControls = container.querySelector('.rhap_main-controls')
expect(mainControls).toBeInTheDocument()
})
it('hides jump controls when disabled', () => {
const { container } = render(<H5AudioPlayer showJumpControls={false} />)
const mainControls = container.querySelector('.rhap_main-controls')
expect(mainControls).toBeInTheDocument()
})
})
describe('Custom Icons', () => {
const customIcons = {
play: <span data-testid="custom-play">Play</span>,
pause: <span data-testid="custom-pause">Pause</span>,
loop: <span data-testid="custom-loop">Loop</span>,
loopOff: <span data-testid="custom-loop-off">Loop Off</span>,
volume: <span data-testid="custom-volume">Volume</span>,
volumeMute: <span data-testid="custom-volume-mute">Volume Mute</span>,
}
it('renders custom play icon when paused', () => {
render(<H5AudioPlayer customIcons={customIcons} />)
expect(screen.getByTestId('custom-play')).toBeInTheDocument()
})
it('renders custom loop icon when loop is enabled', () => {
render(<H5AudioPlayer customIcons={customIcons} loop />)
expect(screen.getByTestId('custom-loop')).toBeInTheDocument()
})
it('renders custom loop off icon when loop is disabled', () => {
render(<H5AudioPlayer customIcons={customIcons} />)
expect(screen.getByTestId('custom-loop-off')).toBeInTheDocument()
})
it('renders custom volume icon when not muted', () => {
render(<H5AudioPlayer customIcons={customIcons} />)
expect(screen.getByTestId('custom-volume')).toBeInTheDocument()
})
})
describe('Internationalization', () => {
const customI18nAriaLabels = {
player: 'Custom Audio Player',
progressControl: 'Custom Progress Control',
volumeControl: 'Custom Volume Control',
play: 'Custom Play',
pause: 'Custom Pause',
loop: 'Custom Disable Loop',
loopOff: 'Custom Enable Loop',
volume: 'Custom Mute',
volumeMute: 'Custom Unmute',
}
it('uses default aria labels', () => {
const { container } = render(<H5AudioPlayer />)
expect(container.querySelector('.rhap_container')).toHaveAttribute('aria-label', 'Audio player')
})
it('uses custom aria labels', () => {
const { container } = render(<H5AudioPlayer i18nAriaLabels={customI18nAriaLabels} />)
expect(container.querySelector('.rhap_container')).toHaveAttribute('aria-label', 'Custom Audio Player')
})
})
describe('Playback Controls', () => {
it('renders play button when audio is paused', () => {
const { container } = render(<H5AudioPlayer />)
setupAudioElement(container, { paused: true })
const playButton = container.querySelector('.rhap_play-pause-button')
expect(playButton).toBeInTheDocument()
expect(playButton).toHaveAttribute('aria-label', 'Play')
})
it('renders pause button when audio is playing', () => {
const { container } = render(<H5AudioPlayer />)
// The component starts with Play button, we'll test that it exists
const playButton = container.querySelector('.rhap_play-pause-button')
expect(playButton).toBeInTheDocument()
expect(playButton).toHaveAttribute('aria-label', 'Play')
})
it('handles play error callback', async () => {
const onPlayError = jest.fn()
const { container } = render(<H5AudioPlayer onPlayError={onPlayError} />)
// Test that the callback prop is passed and component renders
expect(container.querySelector('.rhap_play-pause-button')).toBeInTheDocument()
expect(onPlayError).toBeDefined()
})
it('displays loop button', () => {
const { container } = render(<H5AudioPlayer />)
const loopButton = container.querySelector('.rhap_repeat-button')
expect(loopButton).toBeInTheDocument()
})
it('displays volume button', () => {
const { container } = render(<H5AudioPlayer />)
const volumeButton = container.querySelector('.rhap_volume-button')
expect(volumeButton).toBeInTheDocument()
})
})
describe('Keyboard Controls', () => {
it('supports keyboard navigation', () => {
const { container } = render(<H5AudioPlayer />)
const playerContainer = container.querySelector('.rhap_container')
expect(playerContainer).toHaveAttribute('tabindex', '0')
expect(playerContainer).toHaveAttribute('role', 'group')
})
it('can disable keyboard controls', () => {
const { container } = render(<H5AudioPlayer hasDefaultKeyBindings={false} />)
const playerContainer = container.querySelector('.rhap_container')
// This test just ensures the prop is passed and component renders
expect(playerContainer).toBeInTheDocument()
})
it('responds to keyboard events', () => {
const { container } = render(<H5AudioPlayer />)
const playerContainer = container.querySelector('.rhap_container')
// Test that keydown events can be fired without errors (avoiding volume controls)
expect(() => {
fireEvent.keyDown(playerContainer, { key: ' ' })
fireEvent.keyDown(playerContainer, { key: 'ArrowLeft' })
fireEvent.keyDown(playerContainer, { key: 'ArrowRight' })
fireEvent.keyDown(playerContainer, { key: 'l' })
fireEvent.keyDown(playerContainer, { key: 'm' })
}).not.toThrow()
})
it('uses default volume jump step even when volume controls are hidden', () => {
const { container } = render(<H5AudioPlayer customVolumeControls={[]} />)
const audioElement = setupAudioElement(container)
audioElement.volume = 0.5
const playerContainer = container.querySelector('.rhap_container')
fireEvent.keyDown(playerContainer, { key: 'ArrowUp', preventDefault: jest.fn() })
expect(audioElement.volume).toBeCloseTo(0.6)
})
it('ignores non-finite volume jump values', () => {
const { container } = render(<H5AudioPlayer volumeJumpStep={NaN} />)
const audioElement = setupAudioElement(container)
audioElement.volume = 0.5
const playerContainer = container.querySelector('.rhap_container')
fireEvent.keyDown(playerContainer, { key: 'ArrowDown', preventDefault: jest.fn() })
expect(audioElement.volume).toBe(0.5)
})
it('skips volume updates when current volume is non-finite', () => {
const { container } = render(<H5AudioPlayer />)
const audioElement = setupAudioElement(container)
Object.defineProperty(audioElement, 'volume', { value: NaN, writable: true, configurable: true })
const playerContainer = container.querySelector('.rhap_container')
expect(() => {
fireEvent.keyDown(playerContainer, { key: 'ArrowUp', preventDefault: jest.fn() })
fireEvent.keyDown(playerContainer, { key: 'ArrowDown', preventDefault: jest.fn() })
}).not.toThrow()
expect(Number.isNaN(audioElement.volume)).toBe(true)
})
})
describe('Jump Controls', () => {
it('supports custom jump steps configuration', () => {
const progressJumpSteps = { backward: 10, forward: 15 }
const { container } = render(
<H5AudioPlayer progressJumpSteps={progressJumpSteps} />
)
// Just test that the component renders with custom jump steps
expect(container.querySelector('.rhap_container')).toBeInTheDocument()
})
it('supports fallback jump step configuration', () => {
const { container } = render(<H5AudioPlayer progressJumpStep={10} />)
// Just test that the component renders with fallback jump step
expect(container.querySelector('.rhap_container')).toBeInTheDocument()
})
it('has default jump controls enabled', () => {
const { container } = render(<H5AudioPlayer />)
// Check that jump control buttons are rendered
expect(container.querySelector('.rhap_rewind-button')).toBeInTheDocument()
expect(container.querySelector('.rhap_forward-button')).toBeInTheDocument()
})
it('can disable jump controls', () => {
const { container } = render(<H5AudioPlayer showJumpControls={false} />)
// When jump controls are disabled, main controls still exist but without jump buttons
expect(container.querySelector('.rhap_main-controls')).toBeInTheDocument()
})
it('handles error callbacks', () => {
const onChangeCurrentTimeError = jest.fn()
const { container } = render(
<H5AudioPlayer onChangeCurrentTimeError={onChangeCurrentTimeError} />
)
// Test that the component renders with error handler
expect(container.querySelector('.rhap_container')).toBeInTheDocument()
})
})
describe('Event Handlers', () => {
it('supports all event handler props', () => {
const eventHandlers = {
onAbort: jest.fn(),
onCanPlay: jest.fn(),
onCanPlayThrough: jest.fn(),
onEnded: jest.fn(),
onPlaying: jest.fn(),
onSeeking: jest.fn(),
onSeeked: jest.fn(),
onStalled: jest.fn(),
onSuspend: jest.fn(),
onLoadStart: jest.fn(),
onLoadedMetaData: jest.fn(),
onLoadedData: jest.fn(),
onWaiting: jest.fn(),
onEmptied: jest.fn(),
onError: jest.fn(),
onListen: jest.fn(),
onVolumeChange: jest.fn(),
onPause: jest.fn(),
onPlay: jest.fn(),
onClickPrevious: jest.fn(),
onClickNext: jest.fn(),
}
const { container } = render(<H5AudioPlayer {...eventHandlers} />)
expect(container.querySelector('.rhap_container')).toBeInTheDocument()
})
it('supports skip controls with callbacks', () => {
const onClickPrevious = jest.fn()
const onClickNext = jest.fn()
const { container } = render(
<H5AudioPlayer
showSkipControls
onClickPrevious={onClickPrevious}
onClickNext={onClickNext}
/>
)
expect(container.querySelector('.rhap_main-controls')).toBeInTheDocument()
})
it('handles listen interval configuration', () => {
const onListen = jest.fn()
const { container } = render(
<H5AudioPlayer onListen={onListen} listenInterval={100} />
)
expect(container.querySelector('.rhap_container')).toBeInTheDocument()
})
})
describe('MSE Support', () => {
it('supports MSE configuration', () => {
const mseProps = {
onSeek: jest.fn().mockResolvedValue(),
srcDuration: 120,
onEcrypted: jest.fn(),
}
const { container } = render(<H5AudioPlayer mse={mseProps} />)
expect(container.querySelector('.rhap_container')).toBeInTheDocument()
})
it('displays MSE duration when provided', () => {
const mseProps = {
onSeek: jest.fn().mockResolvedValue(),
srcDuration: 120,
}
const { container } = render(<H5AudioPlayer mse={mseProps} />)
const durationElement = container.querySelector('.rhap_total-time')
// MSE props are passed to the component, duration display depends on audio element state
expect(durationElement).toBeInTheDocument()
})
it('supports MSE onSeek callback', async () => {
const onSeek = jest.fn().mockResolvedValue()
const mseProps = { onSeek, srcDuration: 120 }
render(<H5AudioPlayer mse={mseProps} />)
// Test that the callback can be called
await onSeek({}, 60)
expect(onSeek).toHaveBeenCalledWith({}, 60)
})
})
describe('Component Lifecycle', () => {
it('supports autoPlayAfterSrcChange prop', () => {
const { rerender } = render(<H5AudioPlayer src="test1.mp3" autoPlayAfterSrcChange />)
rerender(<H5AudioPlayer src="test2.mp3" autoPlayAfterSrcChange />)
// Test that component can handle src changes
expect(screen.queryByRole('group')).toBeInTheDocument()
})
it('handles src changes without autoplay', () => {
const { rerender } = render(<H5AudioPlayer src="test1.mp3" />)
rerender(<H5AudioPlayer src="test2.mp3" />)
// Test that component can handle src changes
expect(screen.queryByRole('group')).toBeInTheDocument()
})
it('supports initial volume configuration', () => {
const { container } = render(<H5AudioPlayer volume={0.8} />)
expect(container.querySelector('.rhap_container')).toBeInTheDocument()
})
it('supports muted prop', () => {
const { container } = render(<H5AudioPlayer muted />)
expect(container.querySelector('.rhap_container')).toBeInTheDocument()
})
})
describe('Custom UI Modules', () => {
it('renders custom UI module', () => {
const customModule = <div data-testid="custom-module">Custom Content</div>
const customProgressBarSection = [customModule, RHAP_UI.PROGRESS_BAR]
render(<H5AudioPlayer customProgressBarSection={customProgressBarSection} />)
expect(screen.getByTestId('custom-module')).toBeInTheDocument()
})
it('renders custom controls section', () => {
const customModule = <button data-testid="custom-button">Custom Button</button>
const customControlsSection = [RHAP_UI.MAIN_CONTROLS, customModule]
render(<H5AudioPlayer customControlsSection={customControlsSection} />)
expect(screen.getByTestId('custom-button')).toBeInTheDocument()
})
it('renders custom additional controls', () => {
const customModule = <span data-testid="custom-control">Custom Control</span>
const customAdditionalControls = [RHAP_UI.LOOP, customModule]
render(<H5AudioPlayer customAdditionalControls={customAdditionalControls} />)
expect(screen.getByTestId('custom-control')).toBeInTheDocument()
})
it('renders custom volume controls', () => {
const customModule = <div data-testid="custom-volume">Custom Volume</div>
const customVolumeControls = [RHAP_UI.VOLUME, customModule]
render(<H5AudioPlayer customVolumeControls={customVolumeControls} />)
expect(screen.getByTestId('custom-volume')).toBeInTheDocument()
})
})
describe('Time Display', () => {
it('displays default current time when no audio', () => {
const { container } = render(
<H5AudioPlayer defaultCurrentTime="--:--" />
)
expect(container.querySelector('#rhap_current-time').textContent).toBe('--:--')
})
it('displays default duration when no audio', () => {
const { container } = render(
<H5AudioPlayer defaultDuration="--:--" />
)
expect(container.querySelector('.rhap_total-time').textContent).toBe('--:--')
})
it('displays left time correctly', () => {
const customProgressBarSection = [RHAP_UI.CURRENT_LEFT_TIME]
const { container } = render(
<H5AudioPlayer customProgressBarSection={customProgressBarSection} />
)
expect(container.querySelector('#rhap_current-left-time')).toBeInTheDocument()
})
it('uses custom time format', () => {
const { container } = render(
<H5AudioPlayer timeFormat="hh:mm:ss" />
)
// The time format would be handled by CurrentTime and Duration components
expect(container.querySelector('.rhap_total-time')).toBeInTheDocument()
})
})
describe('Progress and Volume Features', () => {
it('shows download progress when enabled', () => {
const { container } = render(<H5AudioPlayer showDownloadProgress />)
expect(container.querySelector('.rhap_progress-bar-show-download')).toBeInTheDocument()
})
it('hides download progress when disabled', () => {
const { container } = render(<H5AudioPlayer showDownloadProgress={false} />)
expect(container.querySelector('.rhap_progress-bar-show-download')).not.toBeInTheDocument()
})
it('shows filled progress when enabled', () => {
render(<H5AudioPlayer showFilledProgress />)
// This would be tested in ProgressBar component tests
})
it('shows filled volume when enabled', () => {
render(<H5AudioPlayer showFilledVolume />)
// This would be tested in VolumeBar component tests
})
})
describe('CSS Classes', () => {
it('applies loop class when loop is enabled', () => {
const { container } = render(<H5AudioPlayer loop />)
expect(container.querySelector('.rhap_container')).toHaveClass('rhap_loop--on')
})
it('applies loop class when loop is disabled', () => {
const { container } = render(<H5AudioPlayer />)
expect(container.querySelector('.rhap_container')).toHaveClass('rhap_loop--off')
})
it('applies play status classes', () => {
const { container } = render(<H5AudioPlayer />)
const playerContainer = container.querySelector('.rhap_container')
// Should have either playing or paused class
expect(
playerContainer.classList.contains('rhap_play-status--playing') ||
playerContainer.classList.contains('rhap_play-status--paused')
).toBe(true)
})
})
describe('Edge Cases', () => {
it('handles play error callback', async () => {
const onPlayError = jest.fn()
const { container } = render(<H5AudioPlayer onPlayError={onPlayError} />)
// Test that the callback prop is passed and component renders
expect(container.querySelector('.rhap_play-pause-button')).toBeInTheDocument()
expect(onPlayError).toBeDefined()
})
it('handles invalid UI module gracefully', () => {
const invalidModule = null
const customProgressBarSection = [RHAP_UI.CURRENT_TIME, invalidModule, RHAP_UI.PROGRESS_BAR]
expect(() => {
render(<H5AudioPlayer customProgressBarSection={customProgressBarSection} />)
}).not.toThrow()
})
it('supports error handling props', () => {
const onError = jest.fn()
const { container } = render(<H5AudioPlayer onError={onError} />)
expect(container.querySelector('.rhap_container')).toBeInTheDocument()
})
it('supports volume jump step configuration', () => {
const { container } = render(<H5AudioPlayer volumeJumpStep={0.5} />)
expect(container.querySelector('.rhap_container')).toBeInTheDocument()
})
})
describe('Accessibility', () => {
it('has proper ARIA attributes', () => {
const { container } = render(<H5AudioPlayer />)
const playerContainer = container.querySelector('.rhap_container')
expect(playerContainer).toHaveAttribute('role', 'group')
expect(playerContainer).toHaveAttribute('aria-label', 'Audio player')
expect(playerContainer).toHaveAttribute('tabindex', '0')
})
it('progress bar has proper ARIA attributes', () => {
const { container } = render(<H5AudioPlayer />)
const progressBar = container.querySelector('.rhap_progress-container')
expect(progressBar).toHaveAttribute('role', 'progressbar')
expect(progressBar).toHaveAttribute('aria-label', 'Audio progress control')
expect(progressBar).toHaveAttribute('aria-valuemin', '0')
expect(progressBar).toHaveAttribute('aria-valuemax', '100')
expect(progressBar).toHaveAttribute('tabindex', '0')
})
it('volume bar has proper ARIA attributes', () => {
const { container } = render(<H5AudioPlayer />)
const volumeBar = container.querySelector('.rhap_volume-bar-area')
expect(volumeBar).toHaveAttribute('role', 'progressbar')
expect(volumeBar).toHaveAttribute('aria-label', 'Volume control')
expect(volumeBar).toHaveAttribute('tabindex', '0')
})
})
describe('Default Props', () => {
it('has correct default jump steps', () => {
expect(H5AudioPlayer.defaultProps.progressJumpSteps).toEqual({
backward: 5000,
forward: 5000,
})
expect(H5AudioPlayer.defaultProps.progressJumpStep).toBe(5000)
})
it('has correct default volume jump step', () => {
expect(H5AudioPlayer.defaultProps.volumeJumpStep).toBe(0.1)
})
it('has correct default i18n labels', () => {
expect(H5AudioPlayer.defaultI18nAriaLabels).toEqual({
player: 'Audio player',
progressControl: 'Audio progress control',
volumeControl: 'Volume control',
play: 'Play',
pause: 'Pause',
rewind: 'Rewind',
forward: 'Forward',
previous: 'Previous',
next: 'Skip',
loop: 'Disable loop',
loopOff: 'Enable loop',
volume: 'Mute',
volumeMute: 'Unmute',
})
})
})
})