mirror of
https://github.com/pezkuwichain/pwap.git
synced 2026-04-22 02:07:55 +00:00
c01abc79df
Added complete testing infrastructure with 160 passing tests across 34 suites: ✅ Test Infrastructure Setup: - Created babel.config.cjs with Expo preset - Configured jest.config.cjs with proper transformIgnorePatterns - Added jest.setup.cjs with comprehensive mocks - Added jest.setup.before.cjs for pre-setup configuration - Created __mocks__/ directory for custom mocks ✅ Component Tests (10 test files): - Badge.test.tsx (13 tests) - 100% coverage - Button.test.tsx (14 tests) - 100% statements - Card.test.tsx (7 tests) - Input.test.tsx (10 tests) - LoadingSkeleton.test.tsx (10 tests) - 93% coverage - TokenIcon.test.tsx (7 tests) - 100% coverage - BottomSheet.test.tsx (9 tests) - index.test.ts (1 test) ✅ Context Tests (4 test files): - AuthContext.test.tsx (7 tests) - PolkadotContext.test.tsx (10 tests) - BiometricAuthContext.test.tsx (11 tests) - LanguageContext.test.tsx (9 tests) ✅ Screen Tests (16 test files): - All major screens tested with provider wrappers - WelcomeScreen, SignIn/SignUp, Dashboard - Wallet, Swap, Staking, Governance - P2P, NFT Gallery, Education, Forum - BeCitizen, Security, Lock, Referral, Profile ✅ Utility Tests: - i18n/index.test.ts (4 tests) - lib/supabase.test.ts (3 tests) - theme/colors.test.ts (2 tests) ✅ App Integration Test: - App.test.tsx (3 tests) Coverage Metrics: - Statements: 37.74% (target: 35%) - Branches: 23.94% (target: 20%) - Functions: 28.53% (target: 25%) - Lines: 39.73% (target: 35%) All coverage thresholds met! ✅ Test Results: - 34/34 test suites passing - 160/160 tests passing - 17 snapshots Key Improvements: - Fixed ProfileScreen.tsx import bug (react-native import) - Added comprehensive mocks for Polkadot, Expo, Supabase - Created test-utils.tsx for provider wrappers - All tests use proper async/await patterns - Proper cleanup with React Testing Library Production Ready: Test infrastructure is complete and extensible.
310 lines
8.5 KiB
TypeScript
310 lines
8.5 KiB
TypeScript
import React from 'react';
|
|
import {
|
|
View,
|
|
Text,
|
|
TouchableOpacity,
|
|
StyleSheet,
|
|
SafeAreaView,
|
|
ScrollView,
|
|
StatusBar,
|
|
Alert,
|
|
} from 'react-native';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { useLanguage } from '../contexts/LanguageContext';
|
|
import { languages } from '../i18n';
|
|
import { KurdistanColors } from '../theme/colors';
|
|
|
|
interface SettingsScreenProps {
|
|
onBack: () => void;
|
|
onLogout: () => void;
|
|
}
|
|
|
|
const SettingsScreen: React.FC<SettingsScreenProps> = ({ onBack, onLogout }) => {
|
|
const { t } = useTranslation();
|
|
const { currentLanguage, changeLanguage } = useLanguage();
|
|
|
|
const handleLanguageChange = async (languageCode: string) => {
|
|
if (languageCode === currentLanguage) return;
|
|
|
|
Alert.alert(
|
|
'Change Language',
|
|
`Switch to ${languages.find(l => l.code === languageCode)?.nativeName}?`,
|
|
[
|
|
{ text: t('common.cancel'), style: 'cancel' },
|
|
{
|
|
text: t('common.confirm'),
|
|
onPress: async () => {
|
|
await changeLanguage(languageCode);
|
|
Alert.alert(
|
|
t('common.success'),
|
|
'Language updated successfully! The app will now use your selected language.'
|
|
);
|
|
},
|
|
},
|
|
]
|
|
);
|
|
};
|
|
|
|
const handleLogout = () => {
|
|
Alert.alert(
|
|
t('settings.logout'),
|
|
'Are you sure you want to logout?',
|
|
[
|
|
{ text: t('common.cancel'), style: 'cancel' },
|
|
{
|
|
text: t('settings.logout'),
|
|
style: 'destructive',
|
|
onPress: onLogout,
|
|
},
|
|
]
|
|
);
|
|
};
|
|
|
|
return (
|
|
<SafeAreaView style={styles.container}>
|
|
<StatusBar barStyle="dark-content" />
|
|
|
|
{/* Header */}
|
|
<View style={styles.header}>
|
|
<TouchableOpacity onPress={onBack} style={styles.backButton}>
|
|
<Text style={styles.backButtonText}>←</Text>
|
|
</TouchableOpacity>
|
|
<Text style={styles.headerTitle}>{t('settings.title')}</Text>
|
|
<View style={styles.placeholder} />
|
|
</View>
|
|
|
|
<ScrollView showsVerticalScrollIndicator={false}>
|
|
{/* Language Section */}
|
|
<View style={styles.section}>
|
|
<Text style={styles.sectionTitle}>{t('settings.language')}</Text>
|
|
{languages.map((language) => (
|
|
<TouchableOpacity
|
|
key={language.code}
|
|
style={[
|
|
styles.languageItem,
|
|
currentLanguage === language.code && styles.languageItemActive,
|
|
]}
|
|
onPress={() => handleLanguageChange(language.code)}
|
|
>
|
|
<View style={styles.languageInfo}>
|
|
<Text style={[
|
|
styles.languageName,
|
|
currentLanguage === language.code && styles.languageNameActive,
|
|
]}>
|
|
{language.nativeName}
|
|
</Text>
|
|
<Text style={styles.languageSubtext}>{language.name}</Text>
|
|
</View>
|
|
{currentLanguage === language.code && (
|
|
<View style={styles.checkmark}>
|
|
<Text style={styles.checkmarkText}>✓</Text>
|
|
</View>
|
|
)}
|
|
</TouchableOpacity>
|
|
))}
|
|
</View>
|
|
|
|
{/* Theme Section */}
|
|
<View style={styles.section}>
|
|
<Text style={styles.sectionTitle}>{t('settings.theme')}</Text>
|
|
<TouchableOpacity style={styles.settingItem}>
|
|
<Text style={styles.settingText}>Dark Mode</Text>
|
|
<Text style={styles.settingValue}>Off</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
|
|
{/* Notifications Section */}
|
|
<View style={styles.section}>
|
|
<Text style={styles.sectionTitle}>{t('settings.notifications')}</Text>
|
|
<TouchableOpacity style={styles.settingItem}>
|
|
<Text style={styles.settingText}>Push Notifications</Text>
|
|
<Text style={styles.settingValue}>Enabled</Text>
|
|
</TouchableOpacity>
|
|
<TouchableOpacity style={styles.settingItem}>
|
|
<Text style={styles.settingText}>Transaction Alerts</Text>
|
|
<Text style={styles.settingValue}>Enabled</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
|
|
{/* Security Section */}
|
|
<View style={styles.section}>
|
|
<Text style={styles.sectionTitle}>{t('settings.security')}</Text>
|
|
<TouchableOpacity style={styles.settingItem}>
|
|
<Text style={styles.settingText}>Biometric Login</Text>
|
|
<Text style={styles.settingValue}>Disabled</Text>
|
|
</TouchableOpacity>
|
|
<TouchableOpacity style={styles.settingItem}>
|
|
<Text style={styles.settingText}>Change Password</Text>
|
|
<Text style={styles.settingValue}>→</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
|
|
{/* About Section */}
|
|
<View style={styles.section}>
|
|
<Text style={styles.sectionTitle}>{t('settings.about')}</Text>
|
|
<View style={styles.settingItem}>
|
|
<Text style={styles.settingText}>Version</Text>
|
|
<Text style={styles.settingValue}>1.0.0</Text>
|
|
</View>
|
|
<TouchableOpacity style={styles.settingItem}>
|
|
<Text style={styles.settingText}>Terms of Service</Text>
|
|
<Text style={styles.settingValue}>→</Text>
|
|
</TouchableOpacity>
|
|
<TouchableOpacity style={styles.settingItem}>
|
|
<Text style={styles.settingText}>Privacy Policy</Text>
|
|
<Text style={styles.settingValue}>→</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
|
|
{/* Logout Button */}
|
|
<TouchableOpacity
|
|
style={styles.logoutButton}
|
|
onPress={handleLogout}
|
|
activeOpacity={0.8}
|
|
>
|
|
<Text style={styles.logoutButtonText}>{t('settings.logout')}</Text>
|
|
</TouchableOpacity>
|
|
|
|
<View style={styles.footer}>
|
|
<Text style={styles.footerText}>
|
|
Pezkuwi Blockchain • {new Date().getFullYear()}
|
|
</Text>
|
|
</View>
|
|
</ScrollView>
|
|
</SafeAreaView>
|
|
);
|
|
};
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
backgroundColor: '#F5F5F5',
|
|
},
|
|
header: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
padding: 20,
|
|
backgroundColor: KurdistanColors.spi,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: '#E0E0E0',
|
|
},
|
|
backButton: {
|
|
width: 40,
|
|
height: 40,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
},
|
|
backButtonText: {
|
|
fontSize: 24,
|
|
color: KurdistanColors.kesk,
|
|
},
|
|
headerTitle: {
|
|
fontSize: 18,
|
|
fontWeight: '600',
|
|
color: KurdistanColors.reş,
|
|
},
|
|
placeholder: {
|
|
width: 40,
|
|
},
|
|
section: {
|
|
marginTop: 20,
|
|
paddingHorizontal: 20,
|
|
},
|
|
sectionTitle: {
|
|
fontSize: 14,
|
|
fontWeight: '600',
|
|
color: '#999',
|
|
marginBottom: 12,
|
|
textTransform: 'uppercase',
|
|
},
|
|
languageItem: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
backgroundColor: KurdistanColors.spi,
|
|
padding: 16,
|
|
borderRadius: 12,
|
|
marginBottom: 8,
|
|
borderWidth: 2,
|
|
borderColor: 'transparent',
|
|
},
|
|
languageItemActive: {
|
|
borderColor: KurdistanColors.kesk,
|
|
backgroundColor: '#F0FAF5',
|
|
},
|
|
languageInfo: {
|
|
flex: 1,
|
|
},
|
|
languageName: {
|
|
fontSize: 16,
|
|
fontWeight: '600',
|
|
color: KurdistanColors.reş,
|
|
marginBottom: 4,
|
|
},
|
|
languageNameActive: {
|
|
color: KurdistanColors.kesk,
|
|
},
|
|
languageSubtext: {
|
|
fontSize: 14,
|
|
color: '#999',
|
|
},
|
|
checkmark: {
|
|
width: 24,
|
|
height: 24,
|
|
borderRadius: 12,
|
|
backgroundColor: KurdistanColors.kesk,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
},
|
|
checkmarkText: {
|
|
color: KurdistanColors.spi,
|
|
fontSize: 14,
|
|
fontWeight: 'bold',
|
|
},
|
|
settingItem: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
backgroundColor: KurdistanColors.spi,
|
|
padding: 16,
|
|
borderRadius: 12,
|
|
marginBottom: 8,
|
|
},
|
|
settingText: {
|
|
fontSize: 16,
|
|
color: KurdistanColors.reş,
|
|
},
|
|
settingValue: {
|
|
fontSize: 16,
|
|
color: '#999',
|
|
},
|
|
logoutButton: {
|
|
backgroundColor: KurdistanColors.sor,
|
|
margin: 20,
|
|
padding: 16,
|
|
borderRadius: 12,
|
|
alignItems: 'center',
|
|
shadowColor: KurdistanColors.sor,
|
|
shadowOffset: { width: 0, height: 4 },
|
|
shadowOpacity: 0.3,
|
|
shadowRadius: 6,
|
|
elevation: 6,
|
|
},
|
|
logoutButtonText: {
|
|
fontSize: 16,
|
|
fontWeight: 'bold',
|
|
color: KurdistanColors.spi,
|
|
},
|
|
footer: {
|
|
alignItems: 'center',
|
|
paddingVertical: 20,
|
|
},
|
|
footerText: {
|
|
fontSize: 12,
|
|
color: '#999',
|
|
},
|
|
});
|
|
|
|
export default SettingsScreen;
|