diff --git a/frontend/testing/unit/components/I18nContext.test.tsx b/frontend/testing/unit/components/I18nContext.test.tsx new file mode 100644 index 00000000..7560d578 --- /dev/null +++ b/frontend/testing/unit/components/I18nContext.test.tsx @@ -0,0 +1,126 @@ +import React from 'react'; +import { describe, it, expect, vi } from 'vitest'; +import { render, screen, act } from '@testing-library/react'; +import { I18nProvider, useTranslation } from '../../../src/components/I18nContext'; + +const LocaleDisplay: React.FC = () => { + const { locale } = useTranslation(); + return
{locale}
; +}; + +const TranslationDisplay: React.FC<{ path: string }> = ({ path }) => { + const { t } = useTranslation(); + return
{t(path)}
; +}; + +const LocaleSwitcher: React.FC<{ targetLocale: string }> = ({ targetLocale }) => { + const { locale, setLocale } = useTranslation(); + return ( + <> +
{locale}
+ + + ); +}; + +describe('I18nContext', () => { + + it('should default locale to "en"', () => { + render( + + + + ); + expect(screen.getByTestId('locale').textContent).toBe('en'); + }); + + it('should return correct translation for a valid nested key', () => { + render( + + + + ); + expect(screen.getByTestId('translation').textContent).toBe('Dashboard'); + }); + + it('should return the key itself when translation path is not found', () => { + render( + + + + ); + expect(screen.getByTestId('translation').textContent).toBe('common.nonexistent'); + }); + + it('should expose setLocale function in context', () => { + render( + + + + ); + expect(screen.getByRole('button', { name: 'Switch' })).toBeDefined(); + }); + + it('should update locale when setLocale is called', async () => { + render( + + + + ); + expect(screen.getByTestId('locale').textContent).toBe('en'); + await act(async () => { + screen.getByRole('button', { name: 'Switch' }).click(); + }); + expect(screen.getByTestId('locale').textContent).toBe('fr'); + }); + + it('should update locale correctly when switching to Hindi', async () => { + render( + + + + ); + await act(async () => { + screen.getByRole('button', { name: 'Switch' }).click(); + }); + expect(screen.getByTestId('locale').textContent).toBe('hi'); + }); + + it('should fail if provider stops exposing locale as a string', () => { + render( + + + + ); + const localeEl = screen.getByTestId('locale'); + expect(typeof localeEl.textContent).toBe('string'); + expect(localeEl.textContent).toBeTruthy(); + }); + + it('should fail if provider stops exposing t as a function', () => { + let capturedT: ((path: string) => string) | undefined; + const Inspector: React.FC = () => { + const { t } = useTranslation(); + capturedT = t; + return null; + }; + render( + + + + ); + expect(typeof capturedT).toBe('function'); + }); + + it('should throw error when useTranslation is used outside I18nProvider', () => { + const BadComponent: React.FC = () => { + useTranslation(); + return null; + }; + const spy = vi.spyOn(console, 'error').mockImplementation(() => {}); + expect(() => render()).toThrow( + 'useTranslation must be used within an I18nProvider' + ); + spy.mockRestore(); + }); +});