Files
TREK/client/tests/unit/i18n/index.test.ts
T
Isaias Tavares f46f484d5f test(i18n): update SUPPORTED_LANGUAGES assertions to use objectContaining
Entries now include a locale field, so exact equality checks were
failing. objectContaining matches on value/label only.
2026-04-12 20:03:57 -03:00

211 lines
7.3 KiB
TypeScript

import { describe, it, expect, beforeEach, vi } from 'vitest'
import { render } from '@testing-library/react'
import React from 'react'
import {
TranslationProvider,
useTranslation,
getLocaleForLanguage,
getIntlLanguage,
isRtlLanguage,
SUPPORTED_LANGUAGES,
} from '../../../src/i18n'
import { resetAllStores, seedStore } from '../../helpers/store'
import { useSettingsStore } from '../../../src/store/settingsStore'
import { buildSettings } from '../../helpers/factories'
beforeEach(() => {
resetAllStores()
vi.clearAllMocks()
})
// ── FE-COMP-I18N-001: Barrel re-exports ───────────────────────────────────────
describe('barrel re-exports', () => {
it('FE-COMP-I18N-001: all named exports are defined with expected types', () => {
expect(TranslationProvider).toBeDefined()
expect(typeof TranslationProvider).toBe('function')
expect(useTranslation).toBeDefined()
expect(typeof useTranslation).toBe('function')
expect(getLocaleForLanguage).toBeDefined()
expect(typeof getLocaleForLanguage).toBe('function')
expect(getIntlLanguage).toBeDefined()
expect(typeof getIntlLanguage).toBe('function')
expect(isRtlLanguage).toBeDefined()
expect(typeof isRtlLanguage).toBe('function')
expect(SUPPORTED_LANGUAGES).toBeDefined()
expect(Array.isArray(SUPPORTED_LANGUAGES)).toBe(true)
})
})
// ── FE-COMP-I18N-002/003: getLocaleForLanguage ────────────────────────────────
describe('getLocaleForLanguage', () => {
it('FE-COMP-I18N-002: returns correct locale for known languages', () => {
expect(getLocaleForLanguage('en')).toBe('en-US')
expect(getLocaleForLanguage('de')).toBe('de-DE')
expect(getLocaleForLanguage('zh-TW')).toBe('zh-TW')
expect(getLocaleForLanguage('ar')).toBe('ar-SA')
expect(getLocaleForLanguage('br')).toBe('pt-BR')
})
it('FE-COMP-I18N-003: falls back to en-US for unknown language codes', () => {
expect(getLocaleForLanguage('xx')).toBe('en-US')
})
})
// ── FE-COMP-I18N-004/005/006: getIntlLanguage ─────────────────────────────────
describe('getIntlLanguage', () => {
it('FE-COMP-I18N-004: returns language code for known supported languages', () => {
expect(getIntlLanguage('de')).toBe('de')
expect(getIntlLanguage('fr')).toBe('fr')
expect(getIntlLanguage('zh-TW')).toBe('zh-TW')
})
it('FE-COMP-I18N-005: maps br to pt-BR', () => {
expect(getIntlLanguage('br')).toBe('pt-BR')
})
it('FE-COMP-I18N-006: falls back to en for unknown codes', () => {
expect(getIntlLanguage('xx')).toBe('en')
})
})
// ── FE-COMP-I18N-007/008: isRtlLanguage ──────────────────────────────────────
describe('isRtlLanguage', () => {
it('FE-COMP-I18N-007: returns true only for Arabic', () => {
expect(isRtlLanguage('ar')).toBe(true)
})
it('FE-COMP-I18N-008: returns false for all other supported languages', () => {
expect(isRtlLanguage('en')).toBe(false)
expect(isRtlLanguage('de')).toBe(false)
expect(isRtlLanguage('zh-TW')).toBe(false)
})
})
// ── FE-COMP-I18N-009: SUPPORTED_LANGUAGES ────────────────────────────────────
describe('SUPPORTED_LANGUAGES', () => {
it('FE-COMP-I18N-009: contains expected entries with value/label shape', () => {
expect(Array.isArray(SUPPORTED_LANGUAGES)).toBe(true)
expect(SUPPORTED_LANGUAGES).toHaveLength(14)
expect(SUPPORTED_LANGUAGES).toContainEqual(expect.objectContaining({ value: 'en', label: 'English' }))
expect(SUPPORTED_LANGUAGES).toContainEqual(expect.objectContaining({ value: 'ar', label: 'العربية' }))
})
})
// ── FE-COMP-I18N-010 to 015: TranslationProvider + useTranslation ─────────────
describe('TranslationProvider + useTranslation integration', () => {
it('FE-COMP-I18N-010: useTranslation returns t, language, and locale', () => {
seedStore(useSettingsStore, { settings: buildSettings({ language: 'en' }) })
let result: { language: string; locale: string; tResult: string } | null = null
function TestComponent() {
const { t, language, locale } = useTranslation()
result = { language, locale, tResult: t('common.loading') }
return null
}
render(
React.createElement(TranslationProvider, null, React.createElement(TestComponent))
)
expect(result).not.toBeNull()
expect(result!.language).toBe('en')
expect(result!.locale).toBe('en-US')
expect(result!.tResult).toBeTruthy()
expect(typeof result!.tResult).toBe('string')
})
it('FE-COMP-I18N-011: t() with params substitutes {count} placeholders', () => {
seedStore(useSettingsStore, { settings: buildSettings({ language: 'en' }) })
let translated = ''
function TestComponent() {
const { t } = useTranslation()
translated = t('dashboard.subtitle.trips', { count: 5, archived: 2 })
return null
}
render(
React.createElement(TranslationProvider, null, React.createElement(TestComponent))
)
expect(translated).toContain('5')
expect(translated).toContain('2')
expect(translated).not.toContain('{count}')
expect(translated).not.toContain('{archived}')
})
it('FE-COMP-I18N-012: TranslationProvider sets document.documentElement.lang', () => {
seedStore(useSettingsStore, { settings: buildSettings({ language: 'de' }) })
function TestComponent() {
useTranslation()
return null
}
render(
React.createElement(TranslationProvider, null, React.createElement(TestComponent))
)
expect(document.documentElement.lang).toBe('de')
})
it('FE-COMP-I18N-013: TranslationProvider sets dir=rtl for Arabic', () => {
seedStore(useSettingsStore, { settings: buildSettings({ language: 'ar' }) })
function TestComponent() {
useTranslation()
return null
}
render(
React.createElement(TranslationProvider, null, React.createElement(TestComponent))
)
expect(document.documentElement.dir).toBe('rtl')
})
it('FE-COMP-I18N-014: TranslationProvider sets dir=ltr for non-RTL language', () => {
seedStore(useSettingsStore, { settings: buildSettings({ language: 'en' }) })
function TestComponent() {
useTranslation()
return null
}
render(
React.createElement(TranslationProvider, null, React.createElement(TestComponent))
)
expect(document.documentElement.dir).toBe('ltr')
})
it('FE-COMP-I18N-015: t() falls back to English for unknown language', () => {
// Seed with a non-existent language to trigger fallback to English translations
seedStore(useSettingsStore, { settings: buildSettings({ language: 'xx' as any }) })
let translated = ''
function TestComponent() {
const { t } = useTranslation()
translated = t('common.loading')
return null
}
render(
React.createElement(TranslationProvider, null, React.createElement(TestComponent))
)
// Should fall back to English translation (non-empty, not the key itself if key exists in en)
expect(typeof translated).toBe('string')
expect(translated.length).toBeGreaterThan(0)
})
})