Add setup progress indicator to onboarding flow
Add SetupProgressIndicator component that shows users their current position in the 4-step onboarding journey: Name → Beneficiary → Equipment → Connect. The indicator displays: - Visual progress bar with percentage fill - Step circles with icons showing completed/current/pending status - Current step label with "Step X of 4" text Integrate the indicator into all four auth screens: - enter-name.tsx (Step 1) - add-loved-one.tsx (Step 2) - purchase.tsx (Step 3) - activate.tsx (Step 4) Also add @expo/vector-icons mock to jest.setup.js for testing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
48019e0b08
commit
dad084c775
170
__tests__/components/SetupProgressIndicator.test.tsx
Normal file
170
__tests__/components/SetupProgressIndicator.test.tsx
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { render } from '@testing-library/react-native';
|
||||||
|
import {
|
||||||
|
SetupProgressIndicator,
|
||||||
|
SetupStep,
|
||||||
|
} from '@/components/SetupProgressIndicator';
|
||||||
|
|
||||||
|
describe('SetupProgressIndicator', () => {
|
||||||
|
describe('Component rendering', () => {
|
||||||
|
it('renders without crashing', () => {
|
||||||
|
const { root } = render(<SetupProgressIndicator currentStep="name" />);
|
||||||
|
expect(root).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders with default variant', () => {
|
||||||
|
const { root } = render(<SetupProgressIndicator currentStep="name" />);
|
||||||
|
expect(root).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders with compact variant', () => {
|
||||||
|
const { root } = render(
|
||||||
|
<SetupProgressIndicator currentStep="name" variant="compact" />
|
||||||
|
);
|
||||||
|
expect(root).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Step progression', () => {
|
||||||
|
const steps: SetupStep[] = ['name', 'beneficiary', 'purchase', 'activate'];
|
||||||
|
|
||||||
|
it.each(steps)('renders correctly for step "%s"', (step) => {
|
||||||
|
const { root } = render(<SetupProgressIndicator currentStep={step} />);
|
||||||
|
expect(root).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows "Step 1 of 4" for name step', () => {
|
||||||
|
const { getByText } = render(<SetupProgressIndicator currentStep="name" />);
|
||||||
|
expect(getByText(/Step 1 of 4/)).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows "Step 2 of 4" for beneficiary step', () => {
|
||||||
|
const { getByText } = render(
|
||||||
|
<SetupProgressIndicator currentStep="beneficiary" />
|
||||||
|
);
|
||||||
|
expect(getByText(/Step 2 of 4/)).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows "Step 3 of 4" for purchase step', () => {
|
||||||
|
const { getByText } = render(
|
||||||
|
<SetupProgressIndicator currentStep="purchase" />
|
||||||
|
);
|
||||||
|
expect(getByText(/Step 3 of 4/)).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows "Step 4 of 4" for activate step', () => {
|
||||||
|
const { getByText } = render(
|
||||||
|
<SetupProgressIndicator currentStep="activate" />
|
||||||
|
);
|
||||||
|
expect(getByText(/Step 4 of 4/)).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Step labels', () => {
|
||||||
|
it('shows step label for name step', () => {
|
||||||
|
const { getByText } = render(<SetupProgressIndicator currentStep="name" />);
|
||||||
|
expect(getByText(/Your Name/)).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows step label for beneficiary step', () => {
|
||||||
|
const { getByText } = render(
|
||||||
|
<SetupProgressIndicator currentStep="beneficiary" />
|
||||||
|
);
|
||||||
|
expect(getByText(/Add Loved One/)).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows step label for purchase step', () => {
|
||||||
|
const { getByText } = render(
|
||||||
|
<SetupProgressIndicator currentStep="purchase" />
|
||||||
|
);
|
||||||
|
expect(getByText(/Get Equipment/)).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows step label for activate step', () => {
|
||||||
|
const { getAllByText } = render(
|
||||||
|
<SetupProgressIndicator currentStep="activate" />
|
||||||
|
);
|
||||||
|
// "Connect" appears as both the short label and in "Step 4 of 4: Connect"
|
||||||
|
expect(getAllByText(/Connect/).length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Progress visualization', () => {
|
||||||
|
it('renders all 4 step circles', () => {
|
||||||
|
const { getAllByTestId, root } = render(
|
||||||
|
<SetupProgressIndicator currentStep="name" />
|
||||||
|
);
|
||||||
|
// Should have 4 step wrappers rendered
|
||||||
|
expect(root).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays short labels in default variant', () => {
|
||||||
|
const { getByText } = render(
|
||||||
|
<SetupProgressIndicator currentStep="beneficiary" />
|
||||||
|
);
|
||||||
|
// Short labels: Name, Person, Equipment, Connect
|
||||||
|
expect(getByText('Name')).toBeDefined();
|
||||||
|
expect(getByText('Person')).toBeDefined();
|
||||||
|
expect(getByText('Equipment')).toBeDefined();
|
||||||
|
expect(getByText('Connect')).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Variant differences', () => {
|
||||||
|
it('default variant shows step labels', () => {
|
||||||
|
const { getByText } = render(<SetupProgressIndicator currentStep="name" />);
|
||||||
|
expect(getByText('Name')).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('compact variant does not show step labels', () => {
|
||||||
|
const { queryByText } = render(
|
||||||
|
<SetupProgressIndicator currentStep="name" variant="compact" />
|
||||||
|
);
|
||||||
|
// In compact mode, short labels should not be rendered
|
||||||
|
// The component conditionally renders labels based on !isCompact
|
||||||
|
// So we expect to NOT find the short labels
|
||||||
|
expect(queryByText('Name')).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Edge cases', () => {
|
||||||
|
it('handles first step correctly', () => {
|
||||||
|
const { getByText } = render(<SetupProgressIndicator currentStep="name" />);
|
||||||
|
expect(getByText(/Step 1 of 4/)).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles last step correctly', () => {
|
||||||
|
const { getByText } = render(
|
||||||
|
<SetupProgressIndicator currentStep="activate" />
|
||||||
|
);
|
||||||
|
expect(getByText(/Step 4 of 4/)).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles middle steps correctly', () => {
|
||||||
|
const { getByText: getByText1 } = render(
|
||||||
|
<SetupProgressIndicator currentStep="beneficiary" />
|
||||||
|
);
|
||||||
|
expect(getByText1(/Step 2 of 4/)).toBeDefined();
|
||||||
|
|
||||||
|
const { getByText: getByText2 } = render(
|
||||||
|
<SetupProgressIndicator currentStep="purchase" />
|
||||||
|
);
|
||||||
|
expect(getByText2(/Step 3 of 4/)).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Accessibility', () => {
|
||||||
|
it('renders with accessible text content', () => {
|
||||||
|
const { getByText } = render(<SetupProgressIndicator currentStep="name" />);
|
||||||
|
// Should have readable step information
|
||||||
|
expect(getByText(/Step 1 of 4: Your Name/)).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows meaningful progress information', () => {
|
||||||
|
const { getByText } = render(
|
||||||
|
<SetupProgressIndicator currentStep="purchase" />
|
||||||
|
);
|
||||||
|
expect(getByText(/Step 3 of 4: Get Equipment/)).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -15,6 +15,7 @@ import { Ionicons } from '@expo/vector-icons';
|
|||||||
import { AppColors, Spacing, BorderRadius, FontSizes, FontWeights } from '@/constants/theme';
|
import { AppColors, Spacing, BorderRadius, FontSizes, FontWeights } from '@/constants/theme';
|
||||||
import { api } from '@/services/api';
|
import { api } from '@/services/api';
|
||||||
import { validateSerial, getSerialErrorMessage } from '@/utils/serialValidation';
|
import { validateSerial, getSerialErrorMessage } from '@/utils/serialValidation';
|
||||||
|
import { SetupProgressIndicator } from '@/components/SetupProgressIndicator';
|
||||||
|
|
||||||
|
|
||||||
export default function ActivateScreen() {
|
export default function ActivateScreen() {
|
||||||
@ -107,6 +108,9 @@ export default function ActivateScreen() {
|
|||||||
<View style={styles.placeholder} />
|
<View style={styles.placeholder} />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
{/* Setup Progress */}
|
||||||
|
<SetupProgressIndicator currentStep="activate" />
|
||||||
|
|
||||||
{/* Icon */}
|
{/* Icon */}
|
||||||
<View style={styles.iconContainer}>
|
<View style={styles.iconContainer}>
|
||||||
<Ionicons name="qr-code" size={64} color={AppColors.primary} />
|
<Ionicons name="qr-code" size={64} color={AppColors.primary} />
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import { Ionicons } from '@expo/vector-icons';
|
|||||||
import * as ImagePicker from 'expo-image-picker';
|
import * as ImagePicker from 'expo-image-picker';
|
||||||
import { Button } from '@/components/ui/Button';
|
import { Button } from '@/components/ui/Button';
|
||||||
import { ErrorMessage } from '@/components/ui/ErrorMessage';
|
import { ErrorMessage } from '@/components/ui/ErrorMessage';
|
||||||
|
import { SetupProgressIndicator } from '@/components/SetupProgressIndicator';
|
||||||
import { AppColors, FontSizes, Spacing, BorderRadius, FontWeights } from '@/constants/theme';
|
import { AppColors, FontSizes, Spacing, BorderRadius, FontWeights } from '@/constants/theme';
|
||||||
import { api } from '@/services/api';
|
import { api } from '@/services/api';
|
||||||
|
|
||||||
@ -151,6 +152,9 @@ export default function AddLovedOneScreen() {
|
|||||||
keyboardShouldPersistTaps="handled"
|
keyboardShouldPersistTaps="handled"
|
||||||
showsVerticalScrollIndicator={false}
|
showsVerticalScrollIndicator={false}
|
||||||
>
|
>
|
||||||
|
{/* Setup Progress */}
|
||||||
|
<SetupProgressIndicator currentStep="beneficiary" />
|
||||||
|
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<View style={styles.header}>
|
<View style={styles.header}>
|
||||||
<Text style={styles.title}>Add a Loved One</Text>
|
<Text style={styles.title}>Add a Loved One</Text>
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import { Ionicons } from '@expo/vector-icons';
|
|||||||
import { Button } from '@/components/ui/Button';
|
import { Button } from '@/components/ui/Button';
|
||||||
import { Input } from '@/components/ui/Input';
|
import { Input } from '@/components/ui/Input';
|
||||||
import { ErrorMessage } from '@/components/ui/ErrorMessage';
|
import { ErrorMessage } from '@/components/ui/ErrorMessage';
|
||||||
|
import { SetupProgressIndicator } from '@/components/SetupProgressIndicator';
|
||||||
import { AppColors, BorderRadius, FontSizes, Spacing } from '@/constants/theme';
|
import { AppColors, BorderRadius, FontSizes, Spacing } from '@/constants/theme';
|
||||||
import { api } from '@/services/api';
|
import { api } from '@/services/api';
|
||||||
|
|
||||||
@ -95,6 +96,9 @@ export default function EnterNameScreen() {
|
|||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
{/* Setup Progress */}
|
||||||
|
<SetupProgressIndicator currentStep="name" />
|
||||||
|
|
||||||
{/* Icon */}
|
{/* Icon */}
|
||||||
<View style={styles.iconContainer}>
|
<View style={styles.iconContainer}>
|
||||||
<View style={styles.iconCircle}>
|
<View style={styles.iconCircle}>
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import { usePaymentSheet } from '@stripe/stripe-react-native';
|
|||||||
import { AppColors, Spacing, BorderRadius, FontSizes, FontWeights, Shadows } from '@/constants/theme';
|
import { AppColors, Spacing, BorderRadius, FontSizes, FontWeights, Shadows } from '@/constants/theme';
|
||||||
import { useAuth } from '@/contexts/AuthContext';
|
import { useAuth } from '@/contexts/AuthContext';
|
||||||
import { api } from '@/services/api';
|
import { api } from '@/services/api';
|
||||||
|
import { SetupProgressIndicator } from '@/components/SetupProgressIndicator';
|
||||||
import { LoadingSpinner } from '@/components/ui/LoadingSpinner';
|
import { LoadingSpinner } from '@/components/ui/LoadingSpinner';
|
||||||
import { hasBeneficiaryDevices } from '@/services/BeneficiaryDetailController';
|
import { hasBeneficiaryDevices } from '@/services/BeneficiaryDetailController';
|
||||||
|
|
||||||
@ -242,6 +243,9 @@ export default function PurchaseScreen() {
|
|||||||
<View style={styles.placeholder} />
|
<View style={styles.placeholder} />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
{/* Setup Progress */}
|
||||||
|
<SetupProgressIndicator currentStep="purchase" />
|
||||||
|
|
||||||
{/* Product Card */}
|
{/* Product Card */}
|
||||||
<View style={styles.productCard}>
|
<View style={styles.productCard}>
|
||||||
<View style={styles.productIcon}>
|
<View style={styles.productIcon}>
|
||||||
|
|||||||
191
components/SetupProgressIndicator.tsx
Normal file
191
components/SetupProgressIndicator.tsx
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text, StyleSheet } from 'react-native';
|
||||||
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
|
import {
|
||||||
|
AppColors,
|
||||||
|
BorderRadius,
|
||||||
|
FontSizes,
|
||||||
|
FontWeights,
|
||||||
|
Spacing,
|
||||||
|
} from '@/constants/theme';
|
||||||
|
|
||||||
|
export type SetupStep = 'name' | 'beneficiary' | 'purchase' | 'activate';
|
||||||
|
|
||||||
|
interface SetupProgressIndicatorProps {
|
||||||
|
currentStep: SetupStep;
|
||||||
|
variant?: 'default' | 'compact';
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StepConfig {
|
||||||
|
key: SetupStep;
|
||||||
|
label: string;
|
||||||
|
shortLabel: string;
|
||||||
|
icon: keyof typeof Ionicons.glyphMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
const STEPS: StepConfig[] = [
|
||||||
|
{ key: 'name', label: 'Your Name', shortLabel: 'Name', icon: 'person-outline' },
|
||||||
|
{ key: 'beneficiary', label: 'Add Loved One', shortLabel: 'Person', icon: 'heart-outline' },
|
||||||
|
{ key: 'purchase', label: 'Get Equipment', shortLabel: 'Equipment', icon: 'hardware-chip-outline' },
|
||||||
|
{ key: 'activate', label: 'Connect', shortLabel: 'Connect', icon: 'wifi-outline' },
|
||||||
|
];
|
||||||
|
|
||||||
|
function getStepIndex(step: SetupStep): number {
|
||||||
|
return STEPS.findIndex((s) => s.key === step);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SetupProgressIndicator({
|
||||||
|
currentStep,
|
||||||
|
variant = 'default',
|
||||||
|
}: SetupProgressIndicatorProps) {
|
||||||
|
const currentIndex = getStepIndex(currentStep);
|
||||||
|
const isCompact = variant === 'compact';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
{/* Progress bar */}
|
||||||
|
<View style={styles.progressBarContainer}>
|
||||||
|
<View style={styles.progressBarTrack}>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
styles.progressBarFill,
|
||||||
|
{ width: `${((currentIndex + 1) / STEPS.length) * 100}%` },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Step indicators */}
|
||||||
|
<View style={styles.stepsContainer}>
|
||||||
|
{STEPS.map((step, index) => {
|
||||||
|
const isCompleted = index < currentIndex;
|
||||||
|
const isCurrent = index === currentIndex;
|
||||||
|
const isPending = index > currentIndex;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View key={step.key} style={styles.stepWrapper}>
|
||||||
|
{/* Step circle */}
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
styles.stepCircle,
|
||||||
|
isCompleted && styles.stepCircleCompleted,
|
||||||
|
isCurrent && styles.stepCircleCurrent,
|
||||||
|
isPending && styles.stepCirclePending,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{isCompleted ? (
|
||||||
|
<Ionicons
|
||||||
|
name="checkmark"
|
||||||
|
size={isCompact ? 12 : 14}
|
||||||
|
color={AppColors.white}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Ionicons
|
||||||
|
name={step.icon}
|
||||||
|
size={isCompact ? 12 : 14}
|
||||||
|
color={isCurrent ? AppColors.white : AppColors.textMuted}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Step label */}
|
||||||
|
{!isCompact && (
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
styles.stepLabel,
|
||||||
|
isCompleted && styles.stepLabelCompleted,
|
||||||
|
isCurrent && styles.stepLabelCurrent,
|
||||||
|
isPending && styles.stepLabelPending,
|
||||||
|
]}
|
||||||
|
numberOfLines={1}
|
||||||
|
>
|
||||||
|
{step.shortLabel}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Current step text */}
|
||||||
|
<Text style={styles.currentStepText}>
|
||||||
|
Step {currentIndex + 1} of {STEPS.length}: {STEPS[currentIndex].label}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
paddingHorizontal: Spacing.md,
|
||||||
|
paddingVertical: Spacing.md,
|
||||||
|
backgroundColor: AppColors.surface,
|
||||||
|
borderRadius: BorderRadius.lg,
|
||||||
|
marginBottom: Spacing.lg,
|
||||||
|
},
|
||||||
|
progressBarContainer: {
|
||||||
|
marginBottom: Spacing.md,
|
||||||
|
},
|
||||||
|
progressBarTrack: {
|
||||||
|
height: 4,
|
||||||
|
backgroundColor: AppColors.borderLight,
|
||||||
|
borderRadius: BorderRadius.full,
|
||||||
|
overflow: 'hidden',
|
||||||
|
},
|
||||||
|
progressBarFill: {
|
||||||
|
height: '100%',
|
||||||
|
backgroundColor: AppColors.primary,
|
||||||
|
borderRadius: BorderRadius.full,
|
||||||
|
},
|
||||||
|
stepsContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
marginBottom: Spacing.sm,
|
||||||
|
},
|
||||||
|
stepWrapper: {
|
||||||
|
alignItems: 'center',
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
stepCircle: {
|
||||||
|
width: 28,
|
||||||
|
height: 28,
|
||||||
|
borderRadius: 14,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
marginBottom: Spacing.xs,
|
||||||
|
},
|
||||||
|
stepCircleCompleted: {
|
||||||
|
backgroundColor: AppColors.success,
|
||||||
|
},
|
||||||
|
stepCircleCurrent: {
|
||||||
|
backgroundColor: AppColors.primary,
|
||||||
|
},
|
||||||
|
stepCirclePending: {
|
||||||
|
backgroundColor: AppColors.backgroundSecondary,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: AppColors.border,
|
||||||
|
},
|
||||||
|
stepLabel: {
|
||||||
|
fontSize: FontSizes.xs,
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
stepLabelCompleted: {
|
||||||
|
color: AppColors.success,
|
||||||
|
fontWeight: FontWeights.medium,
|
||||||
|
},
|
||||||
|
stepLabelCurrent: {
|
||||||
|
color: AppColors.primary,
|
||||||
|
fontWeight: FontWeights.semibold,
|
||||||
|
},
|
||||||
|
stepLabelPending: {
|
||||||
|
color: AppColors.textMuted,
|
||||||
|
},
|
||||||
|
currentStepText: {
|
||||||
|
fontSize: FontSizes.sm,
|
||||||
|
color: AppColors.textSecondary,
|
||||||
|
textAlign: 'center',
|
||||||
|
fontWeight: FontWeights.medium,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default SetupProgressIndicator;
|
||||||
@ -11,10 +11,32 @@ jest.mock('expo-modules-core', () => ({
|
|||||||
NativeModulesProxy: {},
|
NativeModulesProxy: {},
|
||||||
requireNativeViewManager: jest.fn(),
|
requireNativeViewManager: jest.fn(),
|
||||||
requireNativeModule: jest.fn(),
|
requireNativeModule: jest.fn(),
|
||||||
|
requireOptionalNativeModule: jest.fn(() => null),
|
||||||
EventEmitter: class EventEmitter {},
|
EventEmitter: class EventEmitter {},
|
||||||
UnavailabilityError: class UnavailabilityError extends Error {},
|
UnavailabilityError: class UnavailabilityError extends Error {},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Mock @expo/vector-icons
|
||||||
|
jest.mock('@expo/vector-icons', () => {
|
||||||
|
const React = require('react');
|
||||||
|
const MockIcon = (props) => React.createElement('Text', props, props.name);
|
||||||
|
return {
|
||||||
|
Ionicons: MockIcon,
|
||||||
|
MaterialIcons: MockIcon,
|
||||||
|
FontAwesome: MockIcon,
|
||||||
|
Feather: MockIcon,
|
||||||
|
AntDesign: MockIcon,
|
||||||
|
Entypo: MockIcon,
|
||||||
|
EvilIcons: MockIcon,
|
||||||
|
Foundation: MockIcon,
|
||||||
|
MaterialCommunityIcons: MockIcon,
|
||||||
|
Octicons: MockIcon,
|
||||||
|
SimpleLineIcons: MockIcon,
|
||||||
|
Zocial: MockIcon,
|
||||||
|
createIconSet: jest.fn(() => MockIcon),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
// Mock expo winter runtime
|
// Mock expo winter runtime
|
||||||
global.__ExpoImportMetaRegistry = {
|
global.__ExpoImportMetaRegistry = {
|
||||||
register: jest.fn(),
|
register: jest.fn(),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user