feat(i18n): add bilingual support (Català / Castellano)
Run Tests on Branches / Detect Changes (push) Successful in 11s
Run Tests on Branches / Backend Tests (push) Has been skipped
Run Tests on Branches / Frontend Tests (push) Successful in 1m44s
Run Tests on Branches / Frontend Mobile Tests (push) Has been skipped
Run Tests on Branches / Parapharmacy API Tests (push) Has been skipped
Run Tests on Branches / Detect Changes (push) Successful in 11s
Run Tests on Branches / Backend Tests (push) Has been skipped
Run Tests on Branches / Frontend Tests (push) Successful in 1m44s
Run Tests on Branches / Frontend Mobile Tests (push) Has been skipped
Run Tests on Branches / Parapharmacy API Tests (push) Has been skipped
Add lightweight i18n system using React Context + useTranslation hook.
No external dependencies. Language persisted in localStorage('ff-lang').
New files:
- src/i18n/locales/ca.js (~410 translation keys)
- src/i18n/locales/es.js (~410 translation keys)
- src/i18n/LanguageContext.jsx (Provider + t() function)
- src/i18n/useTranslation.js (hook)
- src/i18n/index.js (barrel export)
Modified 22 files:
- main.jsx: wrap App with LanguageProvider
- App.test.jsx: add LanguageProvider wrapper for tests
- 13 user components: BottomNav, HomeView, LoginModal, SearchView,
ProfileView, AlertsView, ScannerView, PharmacyList, MedicineResults,
ProductResults, SavedNotifications, ErrorBoundary, SearchBar
- 6 admin components: AdminView, LoginForm, PharmacyManagement,
MedicineManagement, PharmacyMedicineLink, PharmacyProductLink
ProfileView includes language selector (globe icon + modal CA/ES)
placed next to the Theme selector.
No routes changed. No API calls affected. No navigation changes.
This commit is contained in:
@@ -1,8 +1,13 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
||||
import { render, screen, fireEvent, act } from '@testing-library/react'
|
||||
import { LanguageProvider } from './i18n'
|
||||
import HomeView from './views/HomeView.jsx'
|
||||
import SearchView from './views/SearchView.jsx'
|
||||
|
||||
function Wrapper({ children }) {
|
||||
return <LanguageProvider>{children}</LanguageProvider>
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers()
|
||||
})
|
||||
@@ -16,14 +21,14 @@ describe('HomeView', () => {
|
||||
it('renders two action buttons on the home screen', () => {
|
||||
const onSearch = vi.fn()
|
||||
const onScan = vi.fn()
|
||||
render(<HomeView onSearchClick={onSearch} onScanClick={onScan} />)
|
||||
render(<HomeView onSearchClick={onSearch} onScanClick={onScan} />, { wrapper: Wrapper })
|
||||
expect(screen.getByRole('button', { name: /buscar medicamento/i })).toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: /escanear tsi/i })).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('calls onSearchClick when Buscar Medicamento is clicked', async () => {
|
||||
const onSearch = vi.fn()
|
||||
render(<HomeView onSearchClick={onSearch} onScanClick={vi.fn()} />)
|
||||
render(<HomeView onSearchClick={onSearch} onScanClick={vi.fn()} />, { wrapper: Wrapper })
|
||||
fireEvent.click(screen.getByRole('button', { name: /buscar medicamento/i }))
|
||||
expect(onSearch).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
@@ -31,13 +36,13 @@ describe('HomeView', () => {
|
||||
|
||||
describe('SearchView', () => {
|
||||
it('renders search bar with placeholder', () => {
|
||||
render(<SearchView />)
|
||||
render(<SearchView />, { wrapper: Wrapper })
|
||||
expect(screen.getByPlaceholderText(/escriba el nombre del medicamento/i)).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('does not fetch for queries shorter than 2 chars', async () => {
|
||||
const fetchMock = vi.spyOn(globalThis, 'fetch')
|
||||
render(<SearchView />)
|
||||
render(<SearchView />, { wrapper: Wrapper })
|
||||
|
||||
fireEvent.change(screen.getByPlaceholderText(/escriba el nombre del medicamento/i), {
|
||||
target: { value: 'a' },
|
||||
@@ -59,7 +64,7 @@ describe('SearchView', () => {
|
||||
json: async () => medicines,
|
||||
})
|
||||
|
||||
render(<SearchView />)
|
||||
render(<SearchView />, { wrapper: Wrapper })
|
||||
|
||||
fireEvent.change(screen.getByPlaceholderText(/escriba el nombre del medicamento/i), {
|
||||
target: { value: 'ibu' },
|
||||
@@ -81,7 +86,7 @@ describe('SearchView', () => {
|
||||
json: async () => medicines,
|
||||
})
|
||||
|
||||
render(<SearchView />)
|
||||
render(<SearchView />, { wrapper: Wrapper })
|
||||
const input = screen.getByPlaceholderText(/escriba el nombre del medicamento/i)
|
||||
|
||||
fireEvent.change(input, { target: { value: 'ibu' } })
|
||||
|
||||
Reference in New Issue
Block a user