Files
pwap/mobile/jest.setup.cjs
T
Claude c01abc79df 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.
2025-11-23 06:34:58 +00:00

214 lines
6.2 KiB
JavaScript

// Jest setup for React Native testing
// @testing-library/react-native v12.4+ includes matchers by default
// Disable Expo's winter module system for tests
process.env.EXPO_USE_STATIC_RENDERING = 'true';
global.__ExpoImportMetaRegistry__ = {};
// Mock @react-navigation/native
jest.mock('@react-navigation/native', () => {
const actualNav = jest.requireActual('@react-navigation/native');
return {
...actualNav,
useNavigation: () => ({
navigate: jest.fn(),
goBack: jest.fn(),
setOptions: jest.fn(),
addListener: jest.fn(),
removeListener: jest.fn(),
}),
useRoute: () => ({
params: {},
}),
};
});
// Mock expo modules
jest.mock('expo-linear-gradient', () => ({
LinearGradient: 'LinearGradient',
}));
jest.mock('expo-secure-store', () => ({
setItemAsync: jest.fn(() => Promise.resolve()),
getItemAsync: jest.fn(() => Promise.resolve(null)),
deleteItemAsync: jest.fn(() => Promise.resolve()),
}));
jest.mock('expo-local-authentication', () => ({
authenticateAsync: jest.fn(() =>
Promise.resolve({ success: true })
),
hasHardwareAsync: jest.fn(() => Promise.resolve(true)),
isEnrolledAsync: jest.fn(() => Promise.resolve(true)),
supportedAuthenticationTypesAsync: jest.fn(() => Promise.resolve([1])), // 1 = FINGERPRINT
AuthenticationType: {
FINGERPRINT: 1,
FACIAL_RECOGNITION: 2,
IRIS: 3,
},
}));
// Mock AsyncStorage
jest.mock('@react-native-async-storage/async-storage', () =>
require('@react-native-async-storage/async-storage/jest/async-storage-mock')
);
// Mock Polkadot.js
jest.mock('@polkadot/api', () => ({
ApiPromise: {
create: jest.fn(() =>
Promise.resolve({
isReady: Promise.resolve(true),
query: {},
tx: {},
rpc: {},
disconnect: jest.fn(),
})
),
},
WsProvider: jest.fn(),
}));
// Mock Supabase
jest.mock('./src/lib/supabase', () => ({
supabase: {
auth: {
signInWithPassword: jest.fn(),
signUp: jest.fn(),
signOut: jest.fn(),
getSession: jest.fn(() => Promise.resolve({ data: { session: null }, error: null })),
onAuthStateChange: jest.fn(() => ({
data: {
subscription: {
unsubscribe: jest.fn(),
},
},
})),
},
from: jest.fn(() => ({
select: jest.fn().mockReturnThis(),
insert: jest.fn().mockReturnThis(),
update: jest.fn().mockReturnThis(),
delete: jest.fn().mockReturnThis(),
eq: jest.fn().mockReturnThis(),
order: jest.fn().mockReturnThis(),
single: jest.fn().mockReturnThis(),
})),
},
}));
// Mock shared blockchain utilities
jest.mock('../shared/blockchain/polkadot', () => ({
PEZKUWI_NETWORK: {
name: 'Pezkuwi',
endpoint: 'wss://beta-rpc.pezkuwi.art',
chainId: 'pezkuwi',
},
BLOCKCHAIN_ENDPOINTS: {
mainnet: 'wss://mainnet.pezkuwichain.io',
testnet: 'wss://ws.pezkuwichain.io',
local: 'ws://127.0.0.1:9944',
},
DEFAULT_ENDPOINT: 'ws://127.0.0.1:9944',
getExplorerUrl: jest.fn((txHash) => `https://explorer.pezkuwichain.app/tx/${txHash}`),
}));
// Mock shared DEX utilities
jest.mock('../shared/utils/dex', () => ({
formatTokenBalance: jest.fn((amount, decimals) => '0.00'),
parseTokenInput: jest.fn((input, decimals) => '0'),
calculatePriceImpact: jest.fn(() => '0'),
getAmountOut: jest.fn(() => '0'),
calculateMinAmount: jest.fn((amount, slippage) => '0'),
fetchPools: jest.fn(() => Promise.resolve([])),
fetchUserPositions: jest.fn(() => Promise.resolve([])),
}));
// Mock shared P2P fiat utilities
jest.mock('../shared/lib/p2p-fiat', () => ({
getActiveOffers: jest.fn(() => Promise.resolve([])),
createOffer: jest.fn(() => Promise.resolve({ id: '123' })),
acceptOffer: jest.fn(() => Promise.resolve(true)),
}));
// Mock shared i18n module
jest.mock('../shared/i18n', () => ({
translations: {
en: { welcome: 'Welcome' },
tr: { welcome: 'Hoş geldiniz' },
kmr: { welcome: 'Bi xêr hatî' },
ckb: { welcome: 'بەخێربێن' },
ar: { welcome: 'مرحبا' },
fa: { welcome: 'خوش آمدید' },
},
LANGUAGES: [
{ code: 'en', name: 'English', nativeName: 'English', rtl: false },
{ code: 'tr', name: 'Turkish', nativeName: 'Türkçe', rtl: false },
{ code: 'kmr', name: 'Kurdish Kurmanji', nativeName: 'Kurmancî', rtl: false },
{ code: 'ckb', name: 'Kurdish Sorani', nativeName: 'سۆرانی', rtl: true },
{ code: 'ar', name: 'Arabic', nativeName: 'العربية', rtl: true },
{ code: 'fa', name: 'Persian', nativeName: 'فارسی', rtl: true },
],
DEFAULT_LANGUAGE: 'en',
LANGUAGE_STORAGE_KEY: '@language',
isRTL: jest.fn((code) => ['ckb', 'ar', 'fa'].includes(code)),
}));
// Mock shared wallet utilities (handles import.meta)
jest.mock('../shared/lib/wallet', () => ({
formatBalance: jest.fn((amount, decimals) => '0.00'),
parseBalance: jest.fn((amount) => '0'),
NETWORK_ENDPOINTS: {
local: 'ws://127.0.0.1:9944',
testnet: 'wss://testnet.pezkuwichain.io',
mainnet: 'wss://mainnet.pezkuwichain.io',
staging: 'wss://staging.pezkuwichain.io',
beta: 'wss://rpc.pezkuwichain.io:9944',
},
}));
// Mock shared staking utilities (handles import.meta)
jest.mock('../shared/lib/staking', () => ({
formatBalance: jest.fn((amount) => '0.00'),
NETWORK_ENDPOINTS: {},
}));
// Mock shared citizenship workflow (handles polkadot/extension-dapp)
jest.mock('../shared/lib/citizenship-workflow', () => ({
createCitizenshipRequest: jest.fn(() => Promise.resolve({ id: '123' })),
}));
// Mock react-i18next for i18n initialization
jest.mock('react-i18next', () => ({
...jest.requireActual('react-i18next'),
useTranslation: () => ({
t: (key) => key,
i18n: {
language: 'en',
changeLanguage: jest.fn(() => Promise.resolve()),
isInitialized: true,
},
}),
initReactI18next: {
type: '3rdParty',
init: jest.fn(),
},
}));
// Mock i18next
jest.mock('i18next', () => ({
...jest.requireActual('i18next'),
init: jest.fn(() => Promise.resolve()),
changeLanguage: jest.fn(() => Promise.resolve()),
use: jest.fn(function () { return this; }),
language: 'en',
isInitialized: true,
}));
// Silence console warnings in tests
global.console = {
...console,
warn: jest.fn(),
error: jest.fn(),
};