mirror of
https://github.com/selfxyz/self.git
synced 2026-02-19 02:24:25 -05:00
778 lines
24 KiB
TypeScript
778 lines
24 KiB
TypeScript
// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
|
|
|
import { useNavigation } from '@react-navigation/native';
|
|
import { act, renderHook } from '@testing-library/react-native';
|
|
|
|
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
|
|
|
import { useEarnPointsFlow } from '@/hooks/useEarnPointsFlow';
|
|
import { useRegisterReferral } from '@/hooks/useRegisterReferral';
|
|
import {
|
|
hasUserAnIdentityDocumentRegistered,
|
|
hasUserDoneThePointsDisclosure,
|
|
POINT_VALUES,
|
|
pointsSelfApp,
|
|
} from '@/services/points';
|
|
import useUserStore from '@/stores/userStore';
|
|
import { getModalCallbacks } from '@/utils/modalCallbackRegistry';
|
|
|
|
jest.mock('@react-navigation/native', () => ({
|
|
useNavigation: jest.fn(),
|
|
}));
|
|
|
|
jest.mock('@selfxyz/mobile-sdk-alpha', () => ({
|
|
useSelfClient: jest.fn(),
|
|
}));
|
|
|
|
jest.mock('@/hooks/useRegisterReferral', () => ({
|
|
useRegisterReferral: jest.fn(),
|
|
}));
|
|
|
|
jest.mock('@/services/points', () => ({
|
|
hasUserAnIdentityDocumentRegistered: jest.fn(),
|
|
hasUserDoneThePointsDisclosure: jest.fn(),
|
|
pointsSelfApp: jest.fn(),
|
|
POINT_VALUES: {
|
|
referee: 24,
|
|
},
|
|
}));
|
|
|
|
// userStore is used as-is, no mock needed
|
|
|
|
const mockNavigate = jest.fn();
|
|
const mockUseNavigation = useNavigation as jest.MockedFunction<
|
|
typeof useNavigation
|
|
>;
|
|
const mockUseSelfClient = useSelfClient as jest.MockedFunction<
|
|
typeof useSelfClient
|
|
>;
|
|
const mockUseRegisterReferral = useRegisterReferral as jest.MockedFunction<
|
|
typeof useRegisterReferral
|
|
>;
|
|
const mockHasUserAnIdentityDocumentRegistered =
|
|
hasUserAnIdentityDocumentRegistered as jest.MockedFunction<
|
|
typeof hasUserAnIdentityDocumentRegistered
|
|
>;
|
|
const mockHasUserDoneThePointsDisclosure =
|
|
hasUserDoneThePointsDisclosure as jest.MockedFunction<
|
|
typeof hasUserDoneThePointsDisclosure
|
|
>;
|
|
const mockPointsSelfApp = pointsSelfApp as jest.MockedFunction<
|
|
typeof pointsSelfApp
|
|
>;
|
|
|
|
describe('useEarnPointsFlow', () => {
|
|
const mockSetSelfApp = jest.fn();
|
|
const mockSelfClient = {
|
|
getSelfAppState: jest.fn(() => ({
|
|
setSelfApp: mockSetSelfApp,
|
|
})),
|
|
};
|
|
const mockRegisterReferral = jest.fn();
|
|
const mockSelfApp = {
|
|
appName: '✨ Self Points',
|
|
endpoint: '0x829d183faaa675f8f80e8bb25fb1476cd4f7c1f0',
|
|
sessionId: 'test-session-id',
|
|
};
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
mockSetSelfApp.mockClear();
|
|
jest.useFakeTimers();
|
|
|
|
mockUseNavigation.mockReturnValue({
|
|
navigate: mockNavigate,
|
|
} as any);
|
|
|
|
mockUseSelfClient.mockReturnValue(mockSelfClient as any);
|
|
|
|
mockUseRegisterReferral.mockReturnValue({
|
|
registerReferral: mockRegisterReferral,
|
|
isLoading: false,
|
|
error: null,
|
|
});
|
|
|
|
// Reset user store state
|
|
useUserStore.getState().clearDeepLinkReferrer();
|
|
useUserStore.getState().registeredReferrers.clear();
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.useRealTimers();
|
|
});
|
|
|
|
describe('Identity verification flow', () => {
|
|
it('should show identity verification modal when user has no identity document', async () => {
|
|
mockHasUserAnIdentityDocumentRegistered.mockResolvedValue(false);
|
|
|
|
const { result } = renderHook(() =>
|
|
useEarnPointsFlow({
|
|
hasReferrer: false,
|
|
isReferralConfirmed: undefined,
|
|
}),
|
|
);
|
|
|
|
await act(async () => {
|
|
await result.current.onEarnPointsPress();
|
|
});
|
|
|
|
expect(mockHasUserAnIdentityDocumentRegistered).toHaveBeenCalled();
|
|
expect(mockNavigate).toHaveBeenCalledWith('Modal', {
|
|
titleText: 'Identity Verification Required',
|
|
bodyText:
|
|
'To access Self Points, you need to register an identity document with Self first. This helps us verify your identity and keep your points secure.',
|
|
buttonText: 'Verify Identity',
|
|
secondaryButtonText: 'Not Now',
|
|
callbackId: expect.any(Number),
|
|
});
|
|
});
|
|
|
|
it('should navigate to CountryPicker when identity verification modal button is pressed', async () => {
|
|
mockHasUserAnIdentityDocumentRegistered.mockResolvedValue(false);
|
|
|
|
const { result } = renderHook(() =>
|
|
useEarnPointsFlow({
|
|
hasReferrer: false,
|
|
isReferralConfirmed: undefined,
|
|
}),
|
|
);
|
|
|
|
await act(async () => {
|
|
await result.current.onEarnPointsPress();
|
|
});
|
|
|
|
const callbackId = mockNavigate.mock.calls[0][1].callbackId;
|
|
const callbacks = getModalCallbacks(callbackId);
|
|
|
|
expect(callbacks).toBeDefined();
|
|
|
|
act(() => {
|
|
callbacks!.onButtonPress();
|
|
});
|
|
|
|
act(() => {
|
|
jest.advanceTimersByTime(100);
|
|
});
|
|
|
|
expect(mockNavigate).toHaveBeenCalledWith('CountryPicker');
|
|
});
|
|
|
|
it('should clear referrer when identity verification modal is dismissed with referrer', async () => {
|
|
const referrer = '0x1234567890123456789012345678901234567890';
|
|
useUserStore.getState().setDeepLinkReferrer(referrer);
|
|
mockHasUserAnIdentityDocumentRegistered.mockResolvedValue(false);
|
|
|
|
const { result } = renderHook(() =>
|
|
useEarnPointsFlow({
|
|
hasReferrer: true,
|
|
isReferralConfirmed: undefined,
|
|
}),
|
|
);
|
|
|
|
await act(async () => {
|
|
await result.current.onEarnPointsPress();
|
|
});
|
|
|
|
const callbackId = mockNavigate.mock.calls[0][1].callbackId;
|
|
const callbacks = getModalCallbacks(callbackId);
|
|
|
|
act(() => {
|
|
callbacks!.onModalDismiss();
|
|
});
|
|
|
|
expect(useUserStore.getState().deepLinkReferrer).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe('Points disclosure flow', () => {
|
|
it('should show points disclosure modal when user has not done disclosure', async () => {
|
|
mockHasUserAnIdentityDocumentRegistered.mockResolvedValue(true);
|
|
mockHasUserDoneThePointsDisclosure.mockResolvedValue(false);
|
|
|
|
const { result } = renderHook(() =>
|
|
useEarnPointsFlow({
|
|
hasReferrer: false,
|
|
isReferralConfirmed: undefined,
|
|
}),
|
|
);
|
|
|
|
await act(async () => {
|
|
await result.current.onEarnPointsPress();
|
|
});
|
|
|
|
expect(mockHasUserAnIdentityDocumentRegistered).toHaveBeenCalled();
|
|
expect(mockHasUserDoneThePointsDisclosure).toHaveBeenCalled();
|
|
|
|
expect(mockNavigate).toHaveBeenCalledWith('PointsInfo', {
|
|
showNextButton: true,
|
|
callbackId: expect.any(Number),
|
|
});
|
|
|
|
// We pass callbackId to retrieve and invoke the callback that displays the points disclosure modal
|
|
const callbackId = mockNavigate.mock.calls[0][1].callbackId;
|
|
const callbacks = getModalCallbacks(callbackId);
|
|
|
|
await act(async () => {
|
|
await callbacks!.onButtonPress();
|
|
});
|
|
|
|
expect(mockNavigate).toHaveBeenCalledWith('Modal', {
|
|
titleText: 'Points Disclosure Required',
|
|
bodyText:
|
|
'To access Self Points, you need to complete the points disclosure first. This helps us verify your identity and keep your points secure.',
|
|
buttonText: 'Complete Points Disclosure',
|
|
secondaryButtonText: 'Not Now',
|
|
callbackId: expect.any(Number),
|
|
});
|
|
});
|
|
|
|
it('should navigate to Prove screen when points disclosure modal button is pressed', async () => {
|
|
mockHasUserAnIdentityDocumentRegistered.mockResolvedValue(true);
|
|
mockHasUserDoneThePointsDisclosure.mockResolvedValue(false);
|
|
mockPointsSelfApp.mockResolvedValue(mockSelfApp as any);
|
|
|
|
const { result } = renderHook(() =>
|
|
useEarnPointsFlow({
|
|
hasReferrer: false,
|
|
isReferralConfirmed: undefined,
|
|
}),
|
|
);
|
|
|
|
await act(async () => {
|
|
await result.current.onEarnPointsPress();
|
|
});
|
|
|
|
expect(mockNavigate).toHaveBeenCalledWith('PointsInfo', {
|
|
showNextButton: true,
|
|
callbackId: expect.any(Number),
|
|
});
|
|
|
|
const pointsInfoCallbackId = mockNavigate.mock.calls[0][1].callbackId;
|
|
const pointsInfoCallbacks = getModalCallbacks(pointsInfoCallbackId);
|
|
|
|
await act(async () => {
|
|
await pointsInfoCallbacks!.onButtonPress();
|
|
});
|
|
|
|
const callbackId = mockNavigate.mock.calls[1][1].callbackId;
|
|
const callbacks = getModalCallbacks(callbackId);
|
|
|
|
expect(callbacks).toBeDefined();
|
|
|
|
await act(async () => {
|
|
await callbacks!.onButtonPress();
|
|
});
|
|
|
|
expect(mockPointsSelfApp).toHaveBeenCalled();
|
|
|
|
// setSelfApp is called synchronously after pointsSelfApp resolves
|
|
expect(mockSetSelfApp).toHaveBeenCalledWith(mockSelfApp);
|
|
|
|
act(() => {
|
|
jest.advanceTimersByTime(100);
|
|
});
|
|
|
|
expect(mockNavigate).toHaveBeenCalledWith('ProvingScreenRouter');
|
|
});
|
|
|
|
it('should clear referrer when points disclosure modal is dismissed with referrer', async () => {
|
|
const referrer = '0x1234567890123456789012345678901234567890';
|
|
useUserStore.getState().setDeepLinkReferrer(referrer);
|
|
mockHasUserAnIdentityDocumentRegistered.mockResolvedValue(true);
|
|
mockHasUserDoneThePointsDisclosure.mockResolvedValue(false);
|
|
|
|
const { result } = renderHook(() =>
|
|
useEarnPointsFlow({
|
|
hasReferrer: true,
|
|
isReferralConfirmed: undefined,
|
|
}),
|
|
);
|
|
|
|
await act(async () => {
|
|
await result.current.onEarnPointsPress();
|
|
});
|
|
|
|
expect(mockNavigate).toHaveBeenCalledWith('PointsInfo', {
|
|
showNextButton: true,
|
|
callbackId: expect.any(Number),
|
|
});
|
|
|
|
const pointsInfoCallbackId = mockNavigate.mock.calls[0][1].callbackId;
|
|
const pointsInfoCallbacks = getModalCallbacks(pointsInfoCallbackId);
|
|
|
|
await act(async () => {
|
|
await pointsInfoCallbacks!.onButtonPress();
|
|
});
|
|
|
|
const callbackId = mockNavigate.mock.calls[1][1].callbackId;
|
|
const callbacks = getModalCallbacks(callbackId);
|
|
|
|
act(() => {
|
|
callbacks!.onModalDismiss();
|
|
});
|
|
|
|
expect(useUserStore.getState().deepLinkReferrer).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe('Direct navigation flow', () => {
|
|
it('should navigate to Points screen when user has completed all checks and no referrer', async () => {
|
|
mockHasUserAnIdentityDocumentRegistered.mockResolvedValue(true);
|
|
mockHasUserDoneThePointsDisclosure.mockResolvedValue(true);
|
|
|
|
const { result } = renderHook(() =>
|
|
useEarnPointsFlow({
|
|
hasReferrer: false,
|
|
isReferralConfirmed: undefined,
|
|
}),
|
|
);
|
|
|
|
await act(async () => {
|
|
await result.current.onEarnPointsPress();
|
|
});
|
|
|
|
expect(mockNavigate).toHaveBeenCalledWith('Points');
|
|
});
|
|
|
|
it('should not navigate when user has completed all checks, has referrer, but skipReferralFlow is true', async () => {
|
|
const referrer = '0x1234567890123456789012345678901234567890';
|
|
useUserStore.getState().setDeepLinkReferrer(referrer);
|
|
mockHasUserAnIdentityDocumentRegistered.mockResolvedValue(true);
|
|
mockHasUserDoneThePointsDisclosure.mockResolvedValue(true);
|
|
|
|
const { result } = renderHook(() =>
|
|
useEarnPointsFlow({
|
|
hasReferrer: true,
|
|
isReferralConfirmed: true,
|
|
}),
|
|
);
|
|
|
|
await act(async () => {
|
|
await result.current.onEarnPointsPress(true);
|
|
});
|
|
|
|
// Should not navigate to Points or Gratification
|
|
expect(mockNavigate).not.toHaveBeenCalledWith('Points');
|
|
expect(mockNavigate).not.toHaveBeenCalledWith('Gratification');
|
|
});
|
|
});
|
|
|
|
describe('Referral flow', () => {
|
|
const referrer = '0x1234567890123456789012345678901234567890';
|
|
|
|
beforeEach(() => {
|
|
useUserStore.getState().setDeepLinkReferrer(referrer);
|
|
mockHasUserAnIdentityDocumentRegistered.mockResolvedValue(true);
|
|
mockHasUserDoneThePointsDisclosure.mockResolvedValue(true);
|
|
});
|
|
|
|
it('should handle referral flow when referrer is confirmed and not skipped', async () => {
|
|
mockRegisterReferral.mockResolvedValue({ success: true });
|
|
|
|
const { result } = renderHook(() =>
|
|
useEarnPointsFlow({
|
|
hasReferrer: true,
|
|
isReferralConfirmed: true,
|
|
}),
|
|
);
|
|
|
|
await act(async () => {
|
|
await result.current.onEarnPointsPress(false);
|
|
});
|
|
|
|
expect(mockRegisterReferral).toHaveBeenCalledWith(referrer);
|
|
expect(useUserStore.getState().isReferrerRegistered(referrer)).toBe(true);
|
|
expect(useUserStore.getState().deepLinkReferrer).toBeUndefined();
|
|
expect(mockNavigate).toHaveBeenCalledWith('Gratification', {
|
|
points: POINT_VALUES.referee,
|
|
});
|
|
});
|
|
|
|
it('should not register referral if already registered', async () => {
|
|
useUserStore.getState().markReferrerAsRegistered(referrer);
|
|
|
|
const { result } = renderHook(() =>
|
|
useEarnPointsFlow({
|
|
hasReferrer: true,
|
|
isReferralConfirmed: true,
|
|
}),
|
|
);
|
|
|
|
await act(async () => {
|
|
await result.current.onEarnPointsPress(false);
|
|
});
|
|
|
|
expect(mockRegisterReferral).not.toHaveBeenCalled();
|
|
expect(mockNavigate).toHaveBeenCalledWith('Gratification', {
|
|
points: POINT_VALUES.referee,
|
|
});
|
|
});
|
|
|
|
it('should show error modal and preserve referrer if referral registration fails', async () => {
|
|
mockRegisterReferral.mockResolvedValue({
|
|
success: false,
|
|
error: 'Network error occurred',
|
|
});
|
|
|
|
const originalConsoleError = console.error;
|
|
console.error = jest.fn();
|
|
|
|
const { result } = renderHook(() =>
|
|
useEarnPointsFlow({
|
|
hasReferrer: true,
|
|
isReferralConfirmed: true,
|
|
}),
|
|
);
|
|
|
|
await act(async () => {
|
|
await result.current.onEarnPointsPress(false);
|
|
});
|
|
|
|
expect(mockRegisterReferral).toHaveBeenCalledWith(referrer);
|
|
|
|
// Should NOT navigate to Gratification on failure
|
|
expect(mockNavigate).not.toHaveBeenCalledWith('Gratification', {
|
|
points: POINT_VALUES.referee,
|
|
});
|
|
|
|
// Should show error modal instead
|
|
expect(mockNavigate).toHaveBeenCalledWith('Modal', {
|
|
titleText: 'Referral Registration Failed',
|
|
bodyText: expect.stringContaining('Network error occurred'),
|
|
buttonText: 'Try Again',
|
|
secondaryButtonText: 'Dismiss',
|
|
callbackId: expect.any(Number),
|
|
});
|
|
|
|
// Should preserve the referrer for retry
|
|
expect(useUserStore.getState().deepLinkReferrer).toBe(referrer);
|
|
|
|
// Should log the error
|
|
expect(console.error).toHaveBeenCalledWith(
|
|
'Referral registration failed:',
|
|
'Network error occurred',
|
|
);
|
|
|
|
console.error = originalConsoleError;
|
|
});
|
|
|
|
it('should retry referral registration when error modal retry button is pressed', async () => {
|
|
// First call fails, second call succeeds
|
|
mockRegisterReferral
|
|
.mockResolvedValueOnce({
|
|
success: false,
|
|
error: 'Network error',
|
|
})
|
|
.mockResolvedValueOnce({
|
|
success: true,
|
|
});
|
|
|
|
const originalConsoleError = console.error;
|
|
console.error = jest.fn();
|
|
|
|
const { result } = renderHook(() =>
|
|
useEarnPointsFlow({
|
|
hasReferrer: true,
|
|
isReferralConfirmed: true,
|
|
}),
|
|
);
|
|
|
|
// First attempt - should fail
|
|
await act(async () => {
|
|
await result.current.onEarnPointsPress(false);
|
|
});
|
|
|
|
expect(mockRegisterReferral).toHaveBeenCalledTimes(1);
|
|
expect(mockNavigate).toHaveBeenCalledWith('Modal', {
|
|
titleText: 'Referral Registration Failed',
|
|
bodyText: expect.stringContaining('Network error'),
|
|
buttonText: 'Try Again',
|
|
secondaryButtonText: 'Dismiss',
|
|
callbackId: expect.any(Number),
|
|
});
|
|
|
|
// Referrer should still be in store
|
|
expect(useUserStore.getState().deepLinkReferrer).toBe(referrer);
|
|
|
|
// Get the callback from the error modal and trigger retry
|
|
const callbackId = mockNavigate.mock.calls[0][1].callbackId;
|
|
const callbacks = getModalCallbacks(callbackId);
|
|
|
|
mockNavigate.mockClear();
|
|
|
|
// Retry - should succeed
|
|
await act(async () => {
|
|
await callbacks!.onButtonPress();
|
|
});
|
|
|
|
expect(mockRegisterReferral).toHaveBeenCalledTimes(2);
|
|
expect(mockRegisterReferral).toHaveBeenCalledWith(referrer);
|
|
|
|
// Should now navigate to Gratification
|
|
expect(mockNavigate).toHaveBeenCalledWith('Gratification', {
|
|
points: POINT_VALUES.referee,
|
|
});
|
|
|
|
// Should mark referrer as registered and clear it
|
|
expect(useUserStore.getState().isReferrerRegistered(referrer)).toBe(true);
|
|
expect(useUserStore.getState().deepLinkReferrer).toBeUndefined();
|
|
|
|
console.error = originalConsoleError;
|
|
});
|
|
|
|
it('should clear referrer when error modal is dismissed', async () => {
|
|
mockRegisterReferral.mockResolvedValue({
|
|
success: false,
|
|
error: 'API error',
|
|
});
|
|
|
|
const originalConsoleError = console.error;
|
|
console.error = jest.fn();
|
|
|
|
const { result } = renderHook(() =>
|
|
useEarnPointsFlow({
|
|
hasReferrer: true,
|
|
isReferralConfirmed: true,
|
|
}),
|
|
);
|
|
|
|
await act(async () => {
|
|
await result.current.onEarnPointsPress(false);
|
|
});
|
|
|
|
const callbackId = mockNavigate.mock.calls[0][1].callbackId;
|
|
const callbacks = getModalCallbacks(callbackId);
|
|
|
|
// Dismiss the error modal
|
|
act(() => {
|
|
callbacks!.onModalDismiss();
|
|
});
|
|
|
|
// Referrer should be cleared to prevent retry loop
|
|
expect(useUserStore.getState().deepLinkReferrer).toBeUndefined();
|
|
|
|
console.error = originalConsoleError;
|
|
});
|
|
|
|
it('should not handle referral flow when isReferralConfirmed is false', async () => {
|
|
const { result } = renderHook(() =>
|
|
useEarnPointsFlow({
|
|
hasReferrer: true,
|
|
isReferralConfirmed: false,
|
|
}),
|
|
);
|
|
|
|
await act(async () => {
|
|
await result.current.onEarnPointsPress(false);
|
|
});
|
|
|
|
expect(mockRegisterReferral).not.toHaveBeenCalled();
|
|
expect(mockNavigate).not.toHaveBeenCalledWith('Gratification');
|
|
});
|
|
|
|
it('should not handle referral flow when isReferralConfirmed is undefined', async () => {
|
|
const { result } = renderHook(() =>
|
|
useEarnPointsFlow({
|
|
hasReferrer: true,
|
|
isReferralConfirmed: undefined,
|
|
}),
|
|
);
|
|
|
|
await act(async () => {
|
|
await result.current.onEarnPointsPress(false);
|
|
});
|
|
|
|
expect(mockRegisterReferral).not.toHaveBeenCalled();
|
|
expect(mockNavigate).not.toHaveBeenCalledWith('Gratification');
|
|
});
|
|
|
|
it('should not handle referral flow when hasReferrer is false', async () => {
|
|
useUserStore.getState().clearDeepLinkReferrer();
|
|
|
|
const { result } = renderHook(() =>
|
|
useEarnPointsFlow({
|
|
hasReferrer: false,
|
|
isReferralConfirmed: true,
|
|
}),
|
|
);
|
|
|
|
await act(async () => {
|
|
await result.current.onEarnPointsPress(false);
|
|
});
|
|
|
|
expect(mockRegisterReferral).not.toHaveBeenCalled();
|
|
expect(mockNavigate).not.toHaveBeenCalledWith('Gratification');
|
|
});
|
|
|
|
it('should handle referral flow when referrer is not in store but hasReferrer is true', async () => {
|
|
useUserStore.getState().clearDeepLinkReferrer();
|
|
mockRegisterReferral.mockResolvedValue({ success: true });
|
|
|
|
const { result } = renderHook(() =>
|
|
useEarnPointsFlow({
|
|
hasReferrer: true,
|
|
isReferralConfirmed: true,
|
|
}),
|
|
);
|
|
|
|
await act(async () => {
|
|
await result.current.onEarnPointsPress(false);
|
|
});
|
|
|
|
// Should not call registerReferral if referrer is not in store
|
|
expect(mockRegisterReferral).not.toHaveBeenCalled();
|
|
expect(mockNavigate).not.toHaveBeenCalledWith('Gratification');
|
|
});
|
|
});
|
|
|
|
describe('Edge cases', () => {
|
|
it('should handle errors in hasUserAnIdentityDocumentRegistered gracefully', async () => {
|
|
// Mock to return false on error (as the actual function catches errors and returns false)
|
|
mockHasUserAnIdentityDocumentRegistered.mockResolvedValue(false);
|
|
|
|
const { result } = renderHook(() =>
|
|
useEarnPointsFlow({
|
|
hasReferrer: false,
|
|
isReferralConfirmed: undefined,
|
|
}),
|
|
);
|
|
|
|
await act(async () => {
|
|
await result.current.onEarnPointsPress();
|
|
});
|
|
|
|
// The function catches errors and returns false, so it should show identity verification modal
|
|
expect(mockNavigate).toHaveBeenCalledWith(
|
|
'Modal',
|
|
expect.objectContaining({
|
|
titleText: 'Identity Verification Required',
|
|
callbackId: expect.any(Number),
|
|
}),
|
|
);
|
|
});
|
|
|
|
it('should handle errors in hasUserDoneThePointsDisclosure gracefully', async () => {
|
|
mockHasUserAnIdentityDocumentRegistered.mockResolvedValue(true);
|
|
// Mock to return false on error (as the actual function catches errors and returns false)
|
|
mockHasUserDoneThePointsDisclosure.mockResolvedValue(false);
|
|
|
|
const { result } = renderHook(() =>
|
|
useEarnPointsFlow({
|
|
hasReferrer: false,
|
|
isReferralConfirmed: undefined,
|
|
}),
|
|
);
|
|
|
|
await act(async () => {
|
|
await result.current.onEarnPointsPress();
|
|
});
|
|
|
|
expect(mockNavigate).toHaveBeenCalledWith('PointsInfo', {
|
|
showNextButton: true,
|
|
callbackId: expect.any(Number),
|
|
});
|
|
|
|
const pointsInfoCallbackId = mockNavigate.mock.calls[0][1].callbackId;
|
|
const pointsInfoCallbacks = getModalCallbacks(pointsInfoCallbackId);
|
|
|
|
await act(async () => {
|
|
await pointsInfoCallbacks!.onButtonPress();
|
|
});
|
|
|
|
// The function catches errors and returns false, so it should show points disclosure modal
|
|
expect(mockNavigate).toHaveBeenCalledWith(
|
|
'Modal',
|
|
expect.objectContaining({
|
|
titleText: 'Points Disclosure Required',
|
|
callbackId: expect.any(Number),
|
|
}),
|
|
);
|
|
});
|
|
|
|
it('should call pointsSelfApp when navigating to points proof', async () => {
|
|
mockHasUserAnIdentityDocumentRegistered.mockResolvedValue(true);
|
|
mockHasUserDoneThePointsDisclosure.mockResolvedValue(false);
|
|
mockPointsSelfApp.mockResolvedValue(mockSelfApp as any);
|
|
|
|
const { result } = renderHook(() =>
|
|
useEarnPointsFlow({
|
|
hasReferrer: false,
|
|
isReferralConfirmed: undefined,
|
|
}),
|
|
);
|
|
|
|
await act(async () => {
|
|
await result.current.onEarnPointsPress();
|
|
});
|
|
|
|
expect(mockNavigate).toHaveBeenCalledWith('PointsInfo', {
|
|
showNextButton: true,
|
|
callbackId: expect.any(Number),
|
|
});
|
|
|
|
const pointsInfoCallbackId = mockNavigate.mock.calls[0][1].callbackId;
|
|
const pointsInfoCallbacks = getModalCallbacks(pointsInfoCallbackId);
|
|
|
|
await act(async () => {
|
|
await pointsInfoCallbacks!.onButtonPress();
|
|
});
|
|
|
|
const callbackId = mockNavigate.mock.calls[1][1].callbackId;
|
|
const callbacks = getModalCallbacks(callbackId);
|
|
|
|
await act(async () => {
|
|
await callbacks!.onButtonPress();
|
|
});
|
|
|
|
// Verify pointsSelfApp was called
|
|
expect(mockPointsSelfApp).toHaveBeenCalled();
|
|
|
|
// setSelfApp should be called when pointsSelfApp succeeds
|
|
expect(mockSetSelfApp).toHaveBeenCalledWith(mockSelfApp);
|
|
});
|
|
});
|
|
|
|
describe('Callback dependencies', () => {
|
|
it('should update callbacks when dependencies change', async () => {
|
|
mockHasUserAnIdentityDocumentRegistered.mockResolvedValue(true);
|
|
mockHasUserDoneThePointsDisclosure.mockResolvedValue(true);
|
|
|
|
const referrer = '0x1234567890123456789012345678901234567890';
|
|
useUserStore.getState().setDeepLinkReferrer(referrer);
|
|
mockRegisterReferral.mockResolvedValue({ success: true });
|
|
|
|
const { result, rerender } = renderHook(
|
|
({ hasReferrer, isReferralConfirmed }) =>
|
|
useEarnPointsFlow({ hasReferrer, isReferralConfirmed }),
|
|
{
|
|
initialProps: {
|
|
hasReferrer: false,
|
|
isReferralConfirmed: undefined,
|
|
},
|
|
},
|
|
);
|
|
|
|
await act(async () => {
|
|
await result.current.onEarnPointsPress();
|
|
});
|
|
|
|
expect(mockNavigate).toHaveBeenCalledWith('Points');
|
|
|
|
mockNavigate.mockClear();
|
|
|
|
rerender({
|
|
hasReferrer: true,
|
|
isReferralConfirmed: true,
|
|
});
|
|
|
|
await act(async () => {
|
|
await result.current.onEarnPointsPress(false);
|
|
});
|
|
|
|
expect(mockRegisterReferral).toHaveBeenCalledWith(referrer);
|
|
});
|
|
});
|
|
});
|