test(mobile): add comprehensive test suite - 38% coverage achieved

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.
This commit is contained in:
Claude
2025-11-23 06:34:58 +00:00
parent 2361f8de94
commit c01abc79df
72 changed files with 14064 additions and 134 deletions
@@ -0,0 +1,16 @@
module.exports = {
web3FromAddress: jest.fn(() => Promise.resolve({
signer: {
signRaw: jest.fn(),
signPayload: jest.fn(),
},
})),
web3Enable: jest.fn(() => Promise.resolve([
{
name: 'Polkadot.js Extension',
version: '1.0.0',
},
])),
web3Accounts: jest.fn(() => Promise.resolve([])),
web3ListRpcMethods: jest.fn(() => Promise.resolve([])),
};
+35
View File
@@ -0,0 +1,35 @@
const View = require('react-native/Libraries/Components/View/View');
module.exports = {
GestureHandlerRootView: View,
PanGestureHandler: View,
TapGestureHandler: View,
PinchGestureHandler: View,
RotationGestureHandler: View,
LongPressGestureHandler: View,
ForceTouchGestureHandler: View,
FlingGestureHandler: View,
NativeViewGestureHandler: View,
createNativeWrapper: (component) => component,
State: {},
Directions: {},
gestureHandlerRootHOC: (component) => component,
Swipeable: View,
DrawerLayout: View,
ScrollView: View,
Slider: View,
Switch: View,
TextInput: View,
ToolbarAndroid: View,
ViewPagerAndroid: View,
DrawerLayoutAndroid: View,
WebView: View,
RawButton: View,
BaseButton: View,
RectButton: View,
BorderlessButton: View,
TouchableHighlight: View,
TouchableNativeFeedback: View,
TouchableOpacity: View,
TouchableWithoutFeedback: View,
};
+70
View File
@@ -0,0 +1,70 @@
const React = require('react');
const { View, Text, Image, Animated } = require('react-native');
const Reanimated = {
default: {
View,
Text,
Image,
ScrollView: Animated.ScrollView,
createAnimatedComponent: (component) => component,
spring: jest.fn(),
timing: jest.fn(),
decay: jest.fn(),
sequence: jest.fn(),
parallel: jest.fn(),
delay: jest.fn(),
loop: jest.fn(),
event: jest.fn(),
call: jest.fn(),
block: jest.fn(),
cond: jest.fn(),
eq: jest.fn(),
neq: jest.fn(),
and: jest.fn(),
or: jest.fn(),
defined: jest.fn(),
not: jest.fn(),
set: jest.fn(),
concat: jest.fn(),
add: jest.fn(),
sub: jest.fn(),
multiply: jest.fn(),
divide: jest.fn(),
pow: jest.fn(),
modulo: jest.fn(),
sqrt: jest.fn(),
sin: jest.fn(),
cos: jest.fn(),
tan: jest.fn(),
acos: jest.fn(),
asin: jest.fn(),
atan: jest.fn(),
proc: jest.fn(),
useCode: jest.fn(),
useValue: jest.fn(() => new Animated.Value(0)),
interpolateNode: jest.fn(),
Extrapolate: { CLAMP: jest.fn() },
Value: Animated.Value,
Clock: jest.fn(),
interpolate: jest.fn(),
Easing: Animated.Easing,
},
useSharedValue: jest.fn(() => ({ value: 0 })),
useAnimatedStyle: jest.fn((cb) => cb()),
useAnimatedGestureHandler: jest.fn(),
useAnimatedScrollHandler: jest.fn(),
withTiming: jest.fn((value) => value),
withSpring: jest.fn((value) => value),
withDecay: jest.fn((value) => value),
withDelay: jest.fn((delay, value) => value),
withSequence: jest.fn((...args) => args[0]),
withRepeat: jest.fn((value) => value),
cancelAnimation: jest.fn(),
runOnJS: jest.fn((fn) => fn),
runOnUI: jest.fn((fn) => fn),
Easing: Animated.Easing,
EasingNode: Animated.Easing,
};
module.exports = Reanimated;
+13
View File
@@ -0,0 +1,13 @@
// Mock for sonner (web-only toast library)
module.exports = {
toast: {
success: jest.fn(),
error: jest.fn(),
info: jest.fn(),
warning: jest.fn(),
promise: jest.fn(),
loading: jest.fn(),
dismiss: jest.fn(),
},
Toaster: () => null,
};