diff --git a/__mocks__/expo-keep-awake.ts b/__mocks__/expo-keep-awake.ts new file mode 100644 index 0000000..6ab93ba --- /dev/null +++ b/__mocks__/expo-keep-awake.ts @@ -0,0 +1,3 @@ +export const activateKeepAwakeAsync = jest.fn().mockResolvedValue(undefined); +export const deactivateKeepAwake = jest.fn().mockResolvedValue(undefined); +export const useKeepAwake = jest.fn(); diff --git a/__mocks__/expo-localization.ts b/__mocks__/expo-localization.ts new file mode 100644 index 0000000..4bb5ec9 --- /dev/null +++ b/__mocks__/expo-localization.ts @@ -0,0 +1,28 @@ +export const getLocales = jest.fn().mockReturnValue([ + { + languageCode: 'en', + languageTag: 'en-US', + regionCode: 'US', + textDirection: 'ltr', + digitGroupingSeparator: ',', + decimalSeparator: '.', + measurementSystem: 'imperial', + currencyCode: 'USD', + currencySymbol: '$', + isRTL: false, + }, +]); + +export const getCalendars = jest.fn().mockReturnValue([ + { + calendar: 'gregorian', + timeZone: 'America/New_York', + uses24HourClock: false, + firstWeekday: 1, + }, +]); + +export const locale = 'en-US'; +export const locales = ['en-US']; +export const timezone = 'America/New_York'; +export const isRTL = false; diff --git a/__mocks__/react-native.ts b/__mocks__/react-native.ts index 1942d72..77eac62 100644 --- a/__mocks__/react-native.ts +++ b/__mocks__/react-native.ts @@ -34,6 +34,12 @@ export const Linking = { openURL: jest.fn().mockResolvedValue(undefined), }; +export const I18nManager = { + allowRTL: jest.fn(), + forceRTL: jest.fn(), + isRTL: false, +}; + export const useColorScheme = jest.fn().mockReturnValue('light'); export const useWindowDimensions = jest.fn().mockReturnValue({ @@ -60,4 +66,5 @@ export default { Text, StyleSheet, useColorScheme, + I18nManager, }; diff --git a/jest-setup.ts b/jest-setup.ts index 72f9ba8..aaf5aa8 100644 --- a/jest-setup.ts +++ b/jest-setup.ts @@ -321,6 +321,14 @@ jest.mock('react-native', () => { removeEventListener: jest.fn(), }, + // I18nManager + I18nManager: { + allowRTL: jest.fn(), + forceRTL: jest.fn(), + swapLeftAndRightInRTL: jest.fn(), + isRTL: false, + }, + // NativeEventEmitter NativeEventEmitter: class { addListener = jest.fn(); diff --git a/src/app/login/__tests__/login-form.test.tsx b/src/app/login/__tests__/login-form.test.tsx index 7dc414b..6708e8a 100644 --- a/src/app/login/__tests__/login-form.test.tsx +++ b/src/app/login/__tests__/login-form.test.tsx @@ -113,6 +113,28 @@ jest.mock('@/components/ui/text', () => { }; }); +jest.mock('@/components/ui/select', () => { + const React = jest.requireActual('react'); + const mockView = (name: string) => + React.forwardRef(({ children, ...props }: any, ref: any) => + React.createElement('div', { ...props, ref, testID: name }, children) + ); + return { + Select: React.forwardRef(({ children, onValueChange, selectedValue, ...props }: any, ref: any) => + React.createElement('div', { ...props, ref, testID: 'select' }, children) + ), + SelectTrigger: mockView('select-trigger'), + SelectInput: mockView('select-input'), + SelectIcon: mockView('select-icon'), + SelectPortal: mockView('select-portal'), + SelectBackdrop: mockView('select-backdrop'), + SelectContent: mockView('select-content'), + SelectDragIndicator: mockView('select-drag-indicator'), + SelectDragIndicatorWrapper: mockView('select-drag-indicator-wrapper'), + SelectItem: mockView('select-item'), + }; +}); + // Mock React Native components jest.mock('react-native', () => { const ReactNative = jest.requireActual('react-native'); @@ -126,6 +148,12 @@ jest.mock('react-native', () => { Keyboard: { dismiss: jest.fn(), }, + I18nManager: { + allowRTL: jest.fn(), + forceRTL: jest.fn(), + swapLeftAndRightInRTL: jest.fn(), + isRTL: false, + }, }; }); @@ -134,6 +162,7 @@ jest.mock('lucide-react-native', () => ({ AlertTriangle: jest.fn(() => 'AlertTriangle'), EyeIcon: jest.fn(() => 'EyeIcon'), EyeOffIcon: jest.fn(() => 'EyeOffIcon'), + GlobeIcon: jest.fn(() => 'GlobeIcon'), })); // Mock nativewind @@ -147,6 +176,10 @@ jest.mock('react-i18next', () => ({ useTranslation: () => ({ t: (key: string) => key, }), + initReactI18next: { + type: '3rdParty', + init: jest.fn(), + }, })); // Mock react-hook-form diff --git a/src/app/login/login-form.tsx b/src/app/login/login-form.tsx index 7925f21..1efeb8c 100644 --- a/src/app/login/login-form.tsx +++ b/src/app/login/login-form.tsx @@ -188,10 +188,7 @@ export const LoginForm = ({ onSubmit = () => {}, isLoading = false, error = unde {/* Language Selector */} - setLanguage(val as Language)} selectedValue={language ?? 'en'}> diff --git a/src/translations/ar.json b/src/translations/ar.json index de85aad..1aa3571 100644 --- a/src/translations/ar.json +++ b/src/translations/ar.json @@ -573,6 +573,7 @@ "password": "كلمة المرور", "password_incorrect": "كانت كلمة المرور غير صحيحة", "password_placeholder": "أدخل كلمة المرور الخاصة بك", + "select_language": "اللغة", "sso": { "back": "رجوع", "change_department": "تغيير المستخدم", @@ -598,7 +599,6 @@ "username_placeholder": "أدخل اسم المستخدم أو بريدك الإلكتروني", "username_required": "اسم المستخدم مطلوب" }, - "select_language": "اللغة", "title": "تسجيل الدخول", "username": "اسم المستخدم", "username_placeholder": "أدخل اسم المستخدم الخاص بك" @@ -809,20 +809,17 @@ "background_location": "الموقع في الخلفية", "contact_us": "اتصل بنا", "english": "إنجليزي", - "french": "فرنسي", - "german": "ألماني", - "italian": "إيطالي", - "polish": "بولندي", - "swedish": "سويدي", - "ukrainian": "أوكراني", "enter_password": "أدخل كلمة المرور الخاصة بك", "enter_server_url": "أدخل عنوان URL لواجهة برمجة تطبيقات Resgrid (مثال: https://api.resgrid.com)", "enter_username": "أدخل اسم المستخدم الخاص بك", "environment": "البيئة", + "french": "فرنسي", "general": "عام", "generale": "عام", + "german": "ألماني", "github": "جيثب", "help_center": "مركز المساعدة", + "italian": "إيطالي", "keep_alive": "إبقاء الجهاز نشطاً", "keep_alive_warning": "تحذير: تفعيل إبقاء الجهاز نشطاً سيمنع جهازك من الدخول في وضع السكون وقد يزيد بشكل كبير من استنزاف البطارية.", "keep_screen_on": "إبقاء الشاشة مضاءة", @@ -843,6 +840,7 @@ "notifications_description": "تفعيل الإشعارات لتلقي التنبيهات والتحديثات", "notifications_enable": "تفعيل الإشعارات", "password": "كلمة المرور", + "polish": "بولندي", "preferences": "التفضيلات", "privacy": "سياسة الخصوصية", "privacy_policy": "سياسة الخصوصية", @@ -859,6 +857,7 @@ "status_page": "حالة النظام", "support": "الدعم", "support_us": "ادعمنا", + "swedish": "سويدي", "terms": "شروط الخدمة", "theme": { "dark": "مظلم", @@ -867,6 +866,7 @@ "title": "المظهر" }, "title": "الإعدادات", + "ukrainian": "أوكراني", "unit_selection": "اختيار الوحدة", "username": "اسم المستخدم", "version": "الإصدار", diff --git a/src/translations/en.json b/src/translations/en.json index ce1d071..967836d 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -573,6 +573,7 @@ "password": "Password", "password_incorrect": "Password was incorrect", "password_placeholder": "Enter your password", + "select_language": "Language", "sso": { "back": "Back", "change_department": "Change username", @@ -598,7 +599,6 @@ "username_placeholder": "Enter your username or email", "username_required": "Username is required" }, - "select_language": "Language", "title": "Login", "username": "Username", "username_placeholder": "Enter your username" @@ -809,20 +809,17 @@ "background_location": "Background Location", "contact_us": "Contact Us", "english": "English", - "french": "French", - "german": "German", - "italian": "Italian", - "polish": "Polish", - "swedish": "Swedish", - "ukrainian": "Ukrainian", "enter_password": "Enter your password", "enter_server_url": "Enter Resgrid API URL (e.g., https://api.resgrid.com)", "enter_username": "Enter your username", "environment": "Environment", + "french": "French", "general": "General", "generale": "General", + "german": "German", "github": "Github", "help_center": "Help Center", + "italian": "Italian", "keep_alive": "Keep Alive", "keep_alive_warning": "Warning: Enabling keep alive will prevent your device from sleeping and may significantly increase battery drain.", "keep_screen_on": "Keep Screen On", @@ -843,6 +840,7 @@ "notifications_description": "Enable notifications to receive alerts and updates", "notifications_enable": "Enable Notifications", "password": "Password", + "polish": "Polish", "preferences": "Preferences", "privacy": "Privacy Policy", "privacy_policy": "Privacy Policy", @@ -859,6 +857,7 @@ "status_page": "System Status", "support": "Support", "support_us": "Support Us", + "swedish": "Swedish", "terms": "Terms of Service", "theme": { "dark": "Dark", @@ -867,6 +866,7 @@ "title": "Theme" }, "title": "Settings", + "ukrainian": "Ukrainian", "unit_selection": "Unit Selection", "username": "Username", "version": "Version", diff --git a/src/translations/es.json b/src/translations/es.json index e8a8a52..5e2b70b 100644 --- a/src/translations/es.json +++ b/src/translations/es.json @@ -573,6 +573,7 @@ "password": "Contraseña", "password_incorrect": "La contraseña era incorrecta", "password_placeholder": "Introduce tu contraseña", + "select_language": "Idioma", "sso": { "back": "Atrás", "change_department": "Cambiar usuario", @@ -598,7 +599,6 @@ "username_placeholder": "Introduce tu usuario o correo", "username_required": "El usuario es obligatorio" }, - "select_language": "Idioma", "title": "Iniciar sesión", "username": "Nombre de usuario", "username_placeholder": "Introduce tu nombre de usuario" @@ -809,20 +809,17 @@ "background_location": "Ubicación en segundo plano", "contact_us": "Contáctanos", "english": "Inglés", - "french": "Francés", - "german": "Alemán", - "italian": "Italiano", - "polish": "Polaco", - "swedish": "Sueco", - "ukrainian": "Ucraniano", "enter_password": "Introduce tu contraseña", "enter_server_url": "Introduce la URL de la API de Resgrid (ej: https://api.resgrid.com)", "enter_username": "Introduce tu nombre de usuario", "environment": "Entorno", + "french": "Francés", "general": "General", "generale": "General", + "german": "Alemán", "github": "Github", "help_center": "Centro de ayuda", + "italian": "Italiano", "keep_alive": "Mantener Activo", "keep_alive_warning": "Advertencia: Habilitar mantener activo evitará que su dispositivo entre en suspensión y puede aumentar significativamente el consumo de batería.", "keep_screen_on": "Mantener pantalla encendida", @@ -843,6 +840,7 @@ "notifications_description": "Habilitar notificaciones para recibir alertas y actualizaciones", "notifications_enable": "Habilitar Notificaciones", "password": "Contraseña", + "polish": "Polaco", "preferences": "Preferencias", "privacy": "Política de privacidad", "privacy_policy": "Política de privacidad", @@ -859,6 +857,7 @@ "status_page": "Estado del sistema", "support": "Soporte", "support_us": "Apóyanos", + "swedish": "Sueco", "terms": "Términos de servicio", "theme": { "dark": "Oscuro", @@ -867,6 +866,7 @@ "title": "Tema" }, "title": "Configuración", + "ukrainian": "Ucraniano", "unit_selection": "Selección de unidad", "username": "Nombre de usuario", "version": "Versión", diff --git a/src/translations/pl.json b/src/translations/pl.json index 4d6c110..e775e54 100644 --- a/src/translations/pl.json +++ b/src/translations/pl.json @@ -45,7 +45,7 @@ "selection_error_message": "Nie można zapisać preferowanego urządzenia", "selection_error_title": "Błąd wyboru", "supports_mic_control": "Sterowanie mikrofonem", - "tap_scan_to_find_devices": "Naciśnij „Skanuj", aby znaleźć urządzenia audio Bluetooth", + "tap_scan_to_find_devices": "Naciśnij \u201eSkanuj\u201d, aby znaleźć urządzenia audio Bluetooth", "unknown_device": "Nieznane urządzenie" }, "calendar": {