I have a React component which uses NextJS useRouter and changes the locale and the language of my web application. I want to write the different aspects of my component with different locales. I have two different describe blocks but the second beforeEach is apparently affecting my first beforeEach mocking.
This is my React component:
import { useTranslation } from 'next-i18next';import { useRouter } from 'next/router';import { useEffect, useState } from 'react';type LangProps = 'en' | 'fa';const LanguagesList = () => { const router = useRouter(); const { pathname, asPath, query, locale } = router; const { t: translate } = useTranslation('navbar'); const [lang, setLang] = useState<LangProps>('en'); useEffect(() => { setLang(locale as LangProps); }, [locale]); const changLangHandler = (lngValue: LangProps) => { setLang(lngValue); router.push({ pathname, query }, asPath, { locale: lngValue, }); }; // change just the locale and maintain all other route information including href's query return (<ul className='flex w-24 flex-col'><li className={`relative cursor-pointer text-lg ${ lang === 'en'&&'after:absolute after:-left-3 after:top-0 after:inline-block after:h-full after:w-1 after:bg-accent' }`} onClick={() => changLangHandler('en')}> {translate('English')}</li><li className={`relative cursor-pointer text-lg ${ lang === 'fa'&&'after:absolute after:-right-3 after:top-0 after:inline-block after:h-full after:w-1 after:bg-accent' }`} onClick={() => changLangHandler('fa')}> {translate('Farsi')}</li></ul> );};export default LanguagesList;
My test suite:
/* eslint-disable no-empty-function */import { fireEvent, render, screen,} from '@testing-library/react';import { beforeEach, expect, vi } from 'vitest';import LanguagesList from './LanguagesList';describe('Languages List Component Tests When Locale is en', () => { beforeEach(() => { vi.mock('next/router', () => ({ useRouter() { return { route: '/', pathname: '', query: '', asPath: '', locale: 'en', push: vi.fn(), }; }, })); }); it('should render the component properly initially', () => { render(<LanguagesList />); const languagesListEl = screen.getByRole('list'); expect(languagesListEl).toBeInTheDocument(); }); it('should render list items with length of 2', () => { render(<LanguagesList />); const langListItemEls = screen.getAllByRole('listitem'); langListItemEls.map((item) => { expect(item).toBeInTheDocument(); }); expect(langListItemEls[0].textContent).toBe('English'); expect(langListItemEls[1].textContent).toBe('Farsi'); }); it('should render the correct styles for the first li element when lang is set to en', () => { render(<LanguagesList />); const langListItemEls = screen.getAllByRole('listitem'); langListItemEls[0].getAttribute('class'); screen.debug(); expect(langListItemEls[0]).toHaveClass('after:absolute'); fireEvent.click(langListItemEls[1]); // when clicked it should not have the active classes expect(langListItemEls[0]).not.toHaveClass('after:absolute'); });});describe('Languages List Component Tests When Locale is fa', () => { beforeEach(() => { vi.mock('next/router', () => ({ useRouter() { return { route: '/', pathname: '', query: '', asPath: '', locale: 'fa', push: vi.fn(), }; }, })); }); it('should render the correct styles for the first li element when lang is set to fa', () => { render(<LanguagesList />); const langListItemEls = screen.getAllByRole('listitem'); langListItemEls[0].getAttribute('class'); expect(langListItemEls[0]).not.toHaveClass('after:absolute'); expect(langListItemEls[1]).toHaveClass('after:absolute'); // click on en button fireEvent.click(langListItemEls[0]); // when clicked it should not have the active classes expect(langListItemEls[0]).toHaveClass('after:absolute'); expect(langListItemEls[1]).not.toHaveClass('after:absolute'); });});
I did some researches and found out that vi.mock()
will be hoisted, so I think the second beforeEach will replace the first one and this is why it makes my first describe block tests fail. I tried vi.doMock but it did not work. I want to know what I'm doing wrong in case of mocking the useRouter or anything else.