mirror of
https://github.com/selfxyz/self.git
synced 2026-02-19 02:24:25 -05:00
feat(kyc): register fcm token for sumsub verification (#1673)
* feat(kyc): register fcm token for sumsub verification * fix tests * remove unused import * fix lint
This commit is contained in:
committed by
GitHub
parent
f11e860659
commit
a6c84d80f7
@@ -3,8 +3,9 @@
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import React from 'react';
|
||||
import { v5 as uuidv5 } from 'uuid';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { render } from '@testing-library/react-native';
|
||||
import { fireEvent, render, waitFor } from '@testing-library/react-native';
|
||||
|
||||
import ErrorBoundary from '@/components/ErrorBoundary';
|
||||
import KycSuccessScreen from '@/screens/kyc/KycSuccessScreen';
|
||||
@@ -46,10 +47,6 @@ jest.mock('tamagui', () => ({
|
||||
Text: ({ children, ...props }: any) => <span {...props}>{children}</span>,
|
||||
}));
|
||||
|
||||
jest.mock('@selfxyz/mobile-sdk-alpha', () => ({
|
||||
DelayedLottieView: () => null,
|
||||
}));
|
||||
|
||||
jest.mock('@selfxyz/mobile-sdk-alpha/constants/colors', () => ({
|
||||
black: '#000000',
|
||||
white: '#FFFFFF',
|
||||
@@ -57,17 +54,17 @@ jest.mock('@selfxyz/mobile-sdk-alpha/constants/colors', () => ({
|
||||
|
||||
jest.mock('@selfxyz/mobile-sdk-alpha/components', () => ({
|
||||
AbstractButton: ({ children, onPress }: any) => (
|
||||
<button onClick={onPress} data-testid="abstract-button" type="button">
|
||||
<button onClick={onPress} type="button">
|
||||
{children}
|
||||
</button>
|
||||
),
|
||||
PrimaryButton: ({ children, onPress }: any) => (
|
||||
<button onClick={onPress} data-testid="primary-button" type="button">
|
||||
<button onClick={onPress} type="button">
|
||||
{children}
|
||||
</button>
|
||||
),
|
||||
SecondaryButton: ({ children, onPress }: any) => (
|
||||
<button onClick={onPress} data-testid="secondary-button" type="button">
|
||||
<button onClick={onPress} type="button">
|
||||
{children}
|
||||
</button>
|
||||
),
|
||||
@@ -77,12 +74,23 @@ jest.mock('@selfxyz/mobile-sdk-alpha/components', () => ({
|
||||
),
|
||||
}));
|
||||
|
||||
jest.mock('@selfxyz/mobile-sdk-alpha/constants/analytics', () => ({
|
||||
ProofEvents: {
|
||||
FCM_TOKEN_STORED: 'FCM_TOKEN_STORED',
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('@selfxyz/mobile-sdk-alpha/animations/loading/misc.json', () => ({}));
|
||||
|
||||
jest.mock('@/integrations/haptics', () => ({
|
||||
buttonTap: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('@/services/notifications/notificationService', () => ({
|
||||
requestNotificationPermission: jest.fn(),
|
||||
getFCMToken: jest.fn(),
|
||||
registerDeviceToken: jest.fn(),
|
||||
getSelfUuidNamespace: jest.fn(() => '1eebc0f5-eee9-45a4-9474-a0d103b9f20c'),
|
||||
}));
|
||||
|
||||
jest.mock('@/config/sentry', () => ({
|
||||
@@ -94,12 +102,36 @@ jest.mock('@/services/analytics', () => ({
|
||||
trackNfcEvent: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('@selfxyz/mobile-sdk-alpha', () => ({
|
||||
DelayedLottieView: () => null,
|
||||
useSelfClient: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('@/stores/settingStore', () => ({
|
||||
useSettingStore: jest.fn(),
|
||||
}));
|
||||
|
||||
const mockUseNavigation = useNavigation as jest.MockedFunction<
|
||||
typeof useNavigation
|
||||
>;
|
||||
|
||||
// Import mocked modules
|
||||
const { useSelfClient } = jest.requireMock('@selfxyz/mobile-sdk-alpha');
|
||||
const { useSettingStore } = jest.requireMock('@/stores/settingStore');
|
||||
|
||||
const MOCK_SELF_UUID_NAMESPACE = '1eebc0f5-eee9-45a4-9474-a0d103b9f20c';
|
||||
|
||||
describe('KycSuccessScreen', () => {
|
||||
const mockNavigate = jest.fn();
|
||||
const mockTrackEvent = jest.fn();
|
||||
const mockSetFcmToken = jest.fn();
|
||||
const mockUserId = '19f21362-856a-4606-88e1-fa306036978f';
|
||||
const mockFcmToken = 'mock-fcm-token';
|
||||
const mockRoute = {
|
||||
params: {
|
||||
userId: mockUserId,
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
@@ -107,23 +139,165 @@ describe('KycSuccessScreen', () => {
|
||||
mockUseNavigation.mockReturnValue({
|
||||
navigate: mockNavigate,
|
||||
} as any);
|
||||
|
||||
useSelfClient.mockReturnValue({
|
||||
trackEvent: mockTrackEvent,
|
||||
});
|
||||
|
||||
useSettingStore.mockReturnValue(mockSetFcmToken);
|
||||
|
||||
(
|
||||
notificationService.requestNotificationPermission as jest.Mock
|
||||
).mockResolvedValue(true);
|
||||
(notificationService.getFCMToken as jest.Mock).mockResolvedValue(
|
||||
mockFcmToken,
|
||||
);
|
||||
(notificationService.registerDeviceToken as jest.Mock).mockResolvedValue(
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
it('should render the screen without errors', () => {
|
||||
const { root } = render(<KycSuccessScreen />);
|
||||
const { root } = render(<KycSuccessScreen route={mockRoute} />);
|
||||
expect(root).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should have navigation available', () => {
|
||||
render(<KycSuccessScreen />);
|
||||
render(<KycSuccessScreen route={mockRoute} />);
|
||||
expect(mockUseNavigation).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should have notification service available', () => {
|
||||
render(<KycSuccessScreen />);
|
||||
render(<KycSuccessScreen route={mockRoute} />);
|
||||
expect(notificationService.requestNotificationPermission).toBeDefined();
|
||||
});
|
||||
|
||||
it('should fetch and register FCM token when "Receive live updates" is pressed', async () => {
|
||||
const { root } = render(<KycSuccessScreen route={mockRoute} />);
|
||||
|
||||
const buttons = root.findAllByType('button');
|
||||
const receiveUpdatesButton = buttons[0]; // First button is "Receive live updates"
|
||||
fireEvent.press(receiveUpdatesButton);
|
||||
|
||||
await waitFor(() => {
|
||||
// Verify notification permission was requested
|
||||
expect(
|
||||
notificationService.requestNotificationPermission,
|
||||
).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
// Verify FCM token was fetched
|
||||
expect(notificationService.getFCMToken).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
// Verify FCM token was stored in settings store
|
||||
expect(mockSetFcmToken).toHaveBeenCalledWith(mockFcmToken);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
// Verify tracking event was sent
|
||||
expect(mockTrackEvent).toHaveBeenCalledWith('FCM_TOKEN_STORED');
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
// Verify device token was registered with deterministic session ID
|
||||
expect(notificationService.registerDeviceToken).toHaveBeenCalledWith(
|
||||
uuidv5(mockUserId, MOCK_SELF_UUID_NAMESPACE),
|
||||
mockFcmToken,
|
||||
);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
// Verify navigation to Home screen
|
||||
expect(mockNavigate).toHaveBeenCalledWith('Home', {});
|
||||
});
|
||||
});
|
||||
|
||||
it('should navigate to Home without FCM token when permission is denied', async () => {
|
||||
(
|
||||
notificationService.requestNotificationPermission as jest.Mock
|
||||
).mockResolvedValue(false);
|
||||
|
||||
const { root } = render(<KycSuccessScreen route={mockRoute} />);
|
||||
|
||||
const buttons = root.findAllByType('button');
|
||||
const receiveUpdatesButton = buttons[0]; // First button is "Receive live updates"
|
||||
fireEvent.press(receiveUpdatesButton);
|
||||
|
||||
await waitFor(() => {
|
||||
// Verify notification permission was requested
|
||||
expect(
|
||||
notificationService.requestNotificationPermission,
|
||||
).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
// Verify FCM token was NOT fetched
|
||||
expect(notificationService.getFCMToken).not.toHaveBeenCalled();
|
||||
|
||||
// Verify FCM token was NOT stored
|
||||
expect(mockSetFcmToken).not.toHaveBeenCalled();
|
||||
|
||||
// Verify device token was NOT registered
|
||||
expect(notificationService.registerDeviceToken).not.toHaveBeenCalled();
|
||||
|
||||
await waitFor(() => {
|
||||
// Verify navigation to Home screen still happens
|
||||
expect(mockNavigate).toHaveBeenCalledWith('Home', {});
|
||||
});
|
||||
});
|
||||
|
||||
it('should navigate to Home when "I will check back later" is pressed', () => {
|
||||
const { root } = render(<KycSuccessScreen route={mockRoute} />);
|
||||
|
||||
const buttons = root.findAllByType('button');
|
||||
const checkLaterButton = buttons[1]; // Second button is "I will check back later"
|
||||
fireEvent.press(checkLaterButton);
|
||||
|
||||
// Verify navigation to Home screen
|
||||
expect(mockNavigate).toHaveBeenCalledWith('Home', {});
|
||||
|
||||
// Verify FCM-related functions were NOT called
|
||||
expect(
|
||||
notificationService.requestNotificationPermission,
|
||||
).not.toHaveBeenCalled();
|
||||
expect(notificationService.getFCMToken).not.toHaveBeenCalled();
|
||||
expect(mockSetFcmToken).not.toHaveBeenCalled();
|
||||
expect(notificationService.registerDeviceToken).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle missing userId gracefully', async () => {
|
||||
const routeWithoutUserId = {
|
||||
params: {},
|
||||
};
|
||||
|
||||
(
|
||||
notificationService.requestNotificationPermission as jest.Mock
|
||||
).mockResolvedValue(true);
|
||||
|
||||
const { root } = render(<KycSuccessScreen route={routeWithoutUserId} />);
|
||||
|
||||
const buttons = root.findAllByType('button');
|
||||
const receiveUpdatesButton = buttons[0]; // First button is "Receive live updates"
|
||||
fireEvent.press(receiveUpdatesButton);
|
||||
|
||||
await waitFor(() => {
|
||||
// Verify notification permission was requested
|
||||
expect(
|
||||
notificationService.requestNotificationPermission,
|
||||
).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
// Verify FCM token was NOT fetched (no userId)
|
||||
expect(notificationService.getFCMToken).not.toHaveBeenCalled();
|
||||
|
||||
await waitFor(() => {
|
||||
// Verify navigation to Home screen still happens
|
||||
expect(mockNavigate).toHaveBeenCalledWith('Home', {});
|
||||
});
|
||||
});
|
||||
|
||||
it('renders fallback on render error', () => {
|
||||
// Mock console.error to suppress error boundary error logs during test
|
||||
const consoleErrorSpy = jest
|
||||
|
||||
Reference in New Issue
Block a user