I need help regarding a specific test. I have a Component, that is toggling the sound on and off in my project. The last test, with which I try to test if the e.prevenDefault
gets called does not work, and I cant figure out why.
I hope someone has an idea.
import { useAppSelector, useAppDispatch } from '../../../../app/hooks';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faVolumeXmark} from '@fortawesome/free-solid-svg-icons';
import { faVolumeHigh} from '@fortawesome/free-solid-svg-icons';
import { RootState } from '../../../../app/store';
import { toggleMute } from '../../../../features/soundSlice/soundSlice';
import './SoundBtn.css';
export const SoundBtn = () => {
const isMuted = useAppSelector((state: RootState) => state.sound.isMuted);
const dispatch = useAppDispatch();
// Prevent space key from triggering togglePlay
const handleKeyDown = (e: React.KeyboardEvent<HTMLButtonElement>) => {
if (e.code === ' ' || e.key === 'Space') {
e.preventDefault();
}
};
const togglePlay = () => {
dispatch(toggleMute());
};
return (
<button
className="commandBtn nonHoverBtn"
id='soundBtn'
onClick={togglePlay}
onKeyDown={handleKeyDown}
>
{!isMuted ? (
<FontAwesomeIcon
icon={faVolumeHigh}
className='soundBtnIcon'
aria-label='click to mute sound'
/>
) : (
<FontAwesomeIcon
icon={faVolumeXmark}
className='soundBtnIcon'
aria-label='click to play sound'
/>
)}
</button>
)
};
And this test:
import { fireEvent, screen } from "@testing-library/react";
import { renderWithProviders } from "../../test-utils";
import { SoundBtn } from "../../../components/ui/buttons/soundBtn/SoundBtn";
describe('SoundBtn component', () => {
it('renders the component and the correct button when muted without crashing', () => {
const preloadedState = {
sound: {
isMuted: true,
},
};
renderWithProviders(<SoundBtn />, { preloadedState });
expect(screen.getByLabelText('click to play sound')).toBeInTheDocument();
});
it('renders the component and the correct button when not muted without crashing', () => {
const preloadedState = {
sound: {
isMuted: false,
},
};
renderWithProviders(<SoundBtn />, { preloadedState });
expect(screen.getByLabelText('click to mute sound')).toBeInTheDocument();
});
it('toggles mute, when button is clicked', () => {
const preloadedState = {
sound: {
isMuted: true,
},
};
const { store } = renderWithProviders(<SoundBtn />, { preloadedState });
expect(store.getState().sound.isMuted).toBe(true);
fireEvent.click(screen.getByLabelText('click to play sound'));
const updatedState = store.getState().sound;
expect(updatedState.isMuted).toBe(false);
fireEvent.click(screen.getByLabelText('click to mute sound'));
const newUpdatedState = store.getState().sound;
expect(newUpdatedState.isMuted).toBe(true);
});
it('does not toggle sound, when space key is clicked', () => {
const preloadedState = {
sound: {
isMuted: true,
},
};
const { getByLabelText } = renderWithProviders(<SoundBtn />);
const button = getByLabelText('click to play sound');
const preventDefaultSpy = vi.fn();
const spaceEvent = new KeyboardEvent('keydown', { code: 'Space', key: ' ' });
fireEvent.keyDown(button , spaceEvent);
expect(preventDefaultSpy).toHaveBeenCalled();
const { store } = renderWithProviders(<SoundBtn />, { preloadedState });
expect(store.getState().sound.isMuted).toBe(true);
});
});
For the last test I am getting the error:
FAIL src/__tests__/components/ui/soundBtn.test.tsx > SoundBtn component > does not toggle sound, when space key is clicked AssertionError: expected "spy" to be called at least once ❯ src/__tests__/components/ui/soundBtn.test.tsx:68:35 66| fireEvent.keyDown(button , spaceEvent); 67| 68| expect(preventDefaultSpy).toHaveBeenCalled(); RERUN src/__tests__/components/ui/soundBtn.test.tsx x149
2
Answers
I finally found another workaround without testing the function directly. Instead, I just wrote an assertion that tests the state to not have been changed.
Have you tried to set your buttons onClick event to call the mock function?