INJIMOB-3246 Code coverage for Inji-Wallet repo increase above 5% (#2108)

* INJIMOB-3246 Code coverage for Inji-Wallet repo increase above 5%

Signed-off-by: Kaushik Gupta <kausgpt97@gmail.com>

* INJIMOB-3246: added snapshot tests and coverage increased to +4%

Signed-off-by: Kaushik Gupta <kausgpt97@gmail.com>

* removed duplicated lines

Signed-off-by: Kaushik Gupta <kausgpt97@gmail.com>

* Added updateCredentialInformation tests

Signed-off-by: Kaushik Gupta <kausgpt97@gmail.com>

* added code rabbit changes

Signed-off-by: Kaushik Gupta <kausgpt97@gmail.com>

* removed platform-specific tests without mocking

Signed-off-by: Kaushik Gupta <kausgpt97@gmail.com>

* standardize mocks in VcItemContainerProfileImage tests

Signed-off-by: Kaushik Gupta <kausgpt97@gmail.com>

---------

Signed-off-by: Kaushik Gupta <kausgpt97@gmail.com>
This commit is contained in:
Kaushik Gupta
2025-11-07 11:10:37 +05:30
committed by GitHub
parent 52c7ed1357
commit 33c6caa08a
104 changed files with 25865 additions and 29 deletions

View File

@@ -0,0 +1,42 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {AccountInformation} from './AccountInformation';
describe('AccountInformation Component', () => {
const defaultProps = {
email: 'test@example.com',
picture: 'https://example.com/avatar.jpg',
};
it('should match snapshot with email and picture', () => {
const {toJSON} = render(<AccountInformation {...defaultProps} />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with different email', () => {
const {toJSON} = render(
<AccountInformation {...defaultProps} email="another@test.com" />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with different picture URL', () => {
const {toJSON} = render(
<AccountInformation
{...defaultProps}
picture="https://example.com/different-avatar.jpg"
/>,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with long email', () => {
const {toJSON} = render(
<AccountInformation
{...defaultProps}
email="very.long.email.address@example-domain.com"
/>,
);
expect(toJSON()).toMatchSnapshot();
});
});

View File

@@ -19,7 +19,7 @@ describe('ActivityLog', () => {
describe('getActionText', () => {
let activityLog;
let mockIl18nfn;
let wellknown = {
const wellknown = {
credential_configurations_supported: {
mockId: {
display: [
@@ -67,6 +67,7 @@ describe('getActionText', () => {
activityLog.getActionText(mockIl18nfn, wellknown);
expect(mockIl18nfn).toHaveBeenCalledWith('mockType', {
idType: 'fake VC',
vcStatus: '',
});
expect(mockIl18nfn).toHaveBeenCalledTimes(1);
// TODO: assert the returned string
@@ -81,3 +82,79 @@ describe('getActionText', () => {
expect(mockIl18nfn).toHaveBeenCalledTimes(1);
});
});
describe('VCActivityLog.getLogFromObject', () => {
it('should create VCActivityLog instance from object', () => {
const mockData = {
id: 'test-id',
type: 'VC_ADDED',
timestamp: 1234567890,
deviceName: 'Test Device',
};
const log = VCActivityLog.getLogFromObject(mockData);
expect(log).toBeInstanceOf(VCActivityLog);
expect(log.id).toBe('test-id');
expect(log.type).toBe('VC_ADDED');
expect(log.timestamp).toBe(1234567890);
expect(log.deviceName).toBe('Test Device');
});
it('should create VCActivityLog from empty object', () => {
const log = VCActivityLog.getLogFromObject({});
expect(log).toBeInstanceOf(VCActivityLog);
expect(log.timestamp).toBeDefined();
});
});
describe('VCActivityLog.getActionLabel', () => {
it('should return formatted action label with device name and time', () => {
const mockLog = new VCActivityLog({
deviceName: 'iPhone 12',
timestamp: Date.now() - 60000, // 1 minute ago
});
const label = mockLog.getActionLabel('en');
expect(label).toContain('iPhone 12');
expect(label).toContain('·');
expect(label).toContain('ago');
});
it('should return only time when device name is empty', () => {
const mockLog = new VCActivityLog({
deviceName: '',
timestamp: Date.now() - 120000, // 2 minutes ago
});
const label = mockLog.getActionLabel('en');
expect(label).not.toContain('·');
expect(label).toContain('ago');
});
it('should filter out empty labels', () => {
const mockLog = new VCActivityLog({
deviceName: ' ', // whitespace only
timestamp: Date.now(),
});
const label = mockLog.getActionLabel('en');
expect(label).not.toContain('·');
expect(label).toBeTruthy();
});
it('should format time with device name in English locale', () => {
const mockLog = new VCActivityLog({
deviceName: 'Test Device',
timestamp: Date.now() - 300000, // 5 minutes ago
});
const labelEn = mockLog.getActionLabel('en');
expect(labelEn).toBeTruthy();
expect(labelEn).toContain('Test Device');
});
});

View File

@@ -0,0 +1,65 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {ActivityLogText} from './ActivityLogText';
import {VCItemContainerFlowType} from '../shared/Utils';
import {VCActivityLog} from './ActivityLogEvent';
import {VPShareActivityLog} from './VPShareActivityLogEvent';
// Mock TextItem
jest.mock('./ui/TextItem', () => ({
TextItem: jest.fn(() => null),
}));
// Mock HistoryScreenController
jest.mock('../screens/History/HistoryScreenController', () => ({
useHistoryTab: jest.fn(() => ({
getWellKnownIssuerMap: jest.fn(() => ({display: [{name: 'Test Issuer'}]})),
})),
}));
// Mock ActivityLogEvent
jest.mock('./ActivityLogEvent', () => ({
VCActivityLog: {
getLogFromObject: jest.fn(obj => ({
...obj,
getActionLabel: jest.fn(() => 'Shared'),
getActionText: jest.fn(() => 'Shared with Test Device'),
})),
},
}));
// Mock VPShareActivityLogEvent
jest.mock('./VPShareActivityLogEvent', () => ({
VPShareActivityLog: {
getLogFromObject: jest.fn(obj => ({
...obj,
getActionLabel: jest.fn(() => 'Verified'),
getActionText: jest.fn(() => 'Verified by Test Device'),
})),
},
}));
describe('ActivityLogText Component', () => {
const mockActivity = {
vcLabel: 'Test VC',
timestamp: new Date().toISOString(),
deviceName: 'Test Device',
vcIdType: 'NationalID',
flow: VCItemContainerFlowType.VC_SHARE,
issuer: 'test-issuer',
} as unknown as VCActivityLog;
it('should match snapshot with VC activity', () => {
const {toJSON} = render(<ActivityLogText activity={mockActivity} />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with VP activity', () => {
const vpActivity = {
...mockActivity,
flow: VCItemContainerFlowType.VP_SHARE,
} as unknown as VPShareActivityLog;
const {toJSON} = render(<ActivityLogText activity={vpActivity} />);
expect(toJSON()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,43 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {BackupAndRestoreBannerNotification} from './BackupAndRestoreBannerNotification';
// Mock controllers
jest.mock('../screens/backupAndRestore/BackupController', () => ({
useBackupScreen: jest.fn(() => ({
showBackupInProgress: false,
isBackingUpSuccess: false,
isBackingUpFailure: false,
backupErrorReason: '',
DISMISS: jest.fn(),
DISMISS_SHOW_BACKUP_IN_PROGRESS: jest.fn(),
})),
}));
jest.mock('../screens/Settings/BackupRestoreController', () => ({
useBackupRestoreScreen: jest.fn(() => ({
showRestoreInProgress: false,
isBackUpRestoreSuccess: false,
isBackUpRestoreFailure: false,
restoreErrorReason: '',
DISMISS: jest.fn(),
DISMISS_SHOW_RESTORE_IN_PROGRESS: jest.fn(),
})),
}));
// Mock BannerNotification
jest.mock('./BannerNotification', () => ({
BannerNotification: jest.fn(() => null),
BannerStatusType: {
IN_PROGRESS: 'inProgress',
SUCCESS: 'success',
ERROR: 'error',
},
}));
describe('BackupAndRestoreBannerNotification Component', () => {
it('should match snapshot with no banners', () => {
const {toJSON} = render(<BackupAndRestoreBannerNotification />);
expect(toJSON()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,51 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {BannerNotification, BannerStatusType} from './BannerNotification';
describe('BannerNotification Component', () => {
const defaultProps = {
message: 'Test notification message',
onClosePress: jest.fn(),
testId: 'bannerTest',
type: BannerStatusType.SUCCESS,
};
it('should match snapshot with success status', () => {
const {toJSON} = render(<BannerNotification {...defaultProps} />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with error status', () => {
const {toJSON} = render(
<BannerNotification {...defaultProps} type={BannerStatusType.ERROR} />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with in progress status', () => {
const {toJSON} = render(
<BannerNotification
{...defaultProps}
type={BannerStatusType.IN_PROGRESS}
/>,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with long message', () => {
const {toJSON} = render(
<BannerNotification
{...defaultProps}
message="This is a very long notification message that should wrap to multiple lines and still be displayed correctly"
/>,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with different testId', () => {
const {toJSON} = render(
<BannerNotification {...defaultProps} testId="customBanner" />,
);
expect(toJSON()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,72 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {BannerNotificationContainer} from './BannerNotificationContainer';
// Mock all controllers
jest.mock('./BannerNotificationController', () => ({
UseBannerNotification: jest.fn(() => ({
isBindingSuccess: false,
verificationStatus: null,
isPasscodeUnlock: false,
isBiometricUnlock: false,
isDownloadingFailed: false,
isDownloadingSuccess: false,
isReverificationSuccess: {status: false},
isReverificationFailed: {status: false},
RESET_WALLET_BINDING_SUCCESS: jest.fn(),
RESET_VERIFICATION_STATUS: jest.fn(),
RESET_DOWNLOADING_FAILED: jest.fn(),
RESET_DOWNLOADING_SUCCESS: jest.fn(),
RESET_REVIRIFICATION_SUCCESS: jest.fn(),
RESET_REVERIFICATION_FAILURE: jest.fn(),
DISMISS: jest.fn(),
})),
}));
jest.mock('../screens/Scan/ScanScreenController', () => ({
useScanScreen: jest.fn(() => ({
showQuickShareSuccessBanner: false,
DISMISS_QUICK_SHARE_BANNER: jest.fn(),
})),
}));
jest.mock('../screens/Settings/SettingScreenController', () => ({
useSettingsScreen: jest.fn(() => ({
isKeyOrderSet: null,
RESET_KEY_ORDER_RESPONSE: jest.fn(),
})),
}));
jest.mock('./BackupAndRestoreBannerNotification', () => ({
BackupAndRestoreBannerNotification: jest.fn(() => null),
}));
jest.mock('./BannerNotification', () => ({
BannerNotification: jest.fn(() => null),
BannerStatusType: {
IN_PROGRESS: 'inProgress',
SUCCESS: 'success',
ERROR: 'error',
},
}));
describe('BannerNotificationContainer Component', () => {
it('should match snapshot with no banners visible', () => {
const {toJSON} = render(<BannerNotificationContainer />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with verification banner enabled', () => {
const {toJSON} = render(
<BannerNotificationContainer showVerificationStatusBanner={true} />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with verification banner disabled', () => {
const {toJSON} = render(
<BannerNotificationContainer showVerificationStatusBanner={false} />,
);
expect(toJSON()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,51 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {CopilotTooltip} from './CopilotTooltip';
// Mock ui components
jest.mock('./ui', () => ({
Button: jest.fn(() => null),
Column: ({children}: {children: React.ReactNode}) => <>{children}</>,
Row: ({children}: {children: React.ReactNode}) => <>{children}</>,
Text: ({children}: {children: React.ReactNode}) => <>{children}</>,
}));
// Mock controller
jest.mock('./CopilotTooltipController', () => ({
UseCopilotTooltip: jest.fn(() => ({
copilotEvents: {
on: jest.fn(),
},
SET_TOUR_GUIDE: jest.fn(),
ONBOARDING_DONE: jest.fn(),
INITIAL_DOWNLOAD_DONE: jest.fn(),
CURRENT_STEP: 1,
currentStepTitle: 'Step 1 Title',
currentStepDescription: 'Step 1 Description',
titleTestID: 'stepTitle',
descriptionTestID: 'stepDescription',
stepCount: '1/5',
isFirstStep: true,
isLastStep: false,
isFinalStep: false,
isOnboarding: true,
isInitialDownloading: false,
goToPrev: jest.fn(),
goToNext: jest.fn(),
stop: jest.fn(),
})),
}));
// Mock settings controller
jest.mock('../screens/Settings/SettingScreenController', () => ({
useSettingsScreen: jest.fn(() => ({
BACK: jest.fn(),
})),
}));
describe('CopilotTooltip Component', () => {
it('should match snapshot with first step', () => {
const {toJSON} = render(<CopilotTooltip />);
expect(toJSON()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,39 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {CopyButton} from './CopyButton';
// Mock Clipboard
jest.mock('@react-native-clipboard/clipboard', () => ({
setString: jest.fn(),
}));
// Mock SvgImage
jest.mock('./ui/svg', () => ({
SvgImage: {
copyIcon: jest.fn(() => null),
},
}));
describe('CopyButton Component', () => {
const defaultProps = {
content: 'Test content to copy',
};
it('should match snapshot with default props', () => {
const {toJSON} = render(<CopyButton {...defaultProps} />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with long content', () => {
const {toJSON} = render(
<CopyButton content="This is a very long content string that needs to be copied to clipboard for testing purposes" />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with special characters', () => {
const specialContent = 'Special: @#$%^&*(){}[]';
const {toJSON} = render(<CopyButton content={specialContent} />);
expect(toJSON()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,85 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {DeviceInfoList, DeviceInfo} from './DeviceInfoList';
describe('DeviceInfoList Component', () => {
const mockDeviceInfo: DeviceInfo = {
deviceName: 'Samsung Galaxy S21',
name: 'John Doe',
deviceId: 'device123',
};
it('should render DeviceInfoList component', () => {
const {toJSON} = render(<DeviceInfoList deviceInfo={mockDeviceInfo} />);
expect(toJSON()).toBeTruthy();
});
it('should render with receiver mode', () => {
const {toJSON} = render(
<DeviceInfoList deviceInfo={mockDeviceInfo} of="receiver" />,
);
expect(toJSON()).toBeTruthy();
});
it('should render with sender mode', () => {
const {toJSON} = render(
<DeviceInfoList deviceInfo={mockDeviceInfo} of="sender" />,
);
expect(toJSON()).toBeTruthy();
});
it('should render without of prop', () => {
const {toJSON} = render(<DeviceInfoList deviceInfo={mockDeviceInfo} />);
expect(toJSON()).toBeTruthy();
});
it('should handle different device names', () => {
const deviceNames = [
'iPhone 14 Pro',
'Google Pixel 7',
'OnePlus 11',
'Samsung Galaxy S23',
];
deviceNames.forEach(deviceName => {
const deviceInfo = {...mockDeviceInfo, deviceName};
const {toJSON} = render(<DeviceInfoList deviceInfo={deviceInfo} />);
expect(toJSON()).toBeTruthy();
});
});
it('should handle different user names', () => {
const names = ['Alice Smith', 'Bob Johnson', 'Charlie Brown'];
names.forEach(name => {
const deviceInfo = {...mockDeviceInfo, name};
const {toJSON} = render(<DeviceInfoList deviceInfo={deviceInfo} />);
expect(toJSON()).toBeTruthy();
});
});
it('should handle different device IDs', () => {
const deviceIds = ['device001', 'device002', 'device003'];
deviceIds.forEach(deviceId => {
const deviceInfo = {...mockDeviceInfo, deviceId};
const {toJSON} = render(<DeviceInfoList deviceInfo={deviceInfo} />);
expect(toJSON()).toBeTruthy();
});
});
it('should handle empty device name', () => {
const deviceInfo = {...mockDeviceInfo, deviceName: ''};
const {toJSON} = render(<DeviceInfoList deviceInfo={deviceInfo} />);
expect(toJSON()).toBeTruthy();
});
it('should handle long device names', () => {
const deviceInfo = {
...mockDeviceInfo,
deviceName: 'Very Long Device Name With Many Characters',
};
const {toJSON} = render(<DeviceInfoList deviceInfo={deviceInfo} />);
expect(toJSON()).toBeTruthy();
});
});

View File

@@ -0,0 +1,50 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {DropdownIcon} from './DropdownIcon';
// Mock Popable
jest.mock('react-native-popable', () => ({
Popable: ({children}: {children: React.ReactNode}) => <>{children}</>,
}));
describe('DropdownIcon Component', () => {
const mockItems = [
{label: 'Item 1', onPress: jest.fn(), icon: 'account'},
{label: 'Item 2', onPress: jest.fn(), icon: 'settings'},
{label: 'Item 3', onPress: jest.fn(), icon: 'delete', idType: 'type1'},
];
const defaultProps = {
idType: 'type1',
icon: 'dots-vertical',
items: mockItems,
};
it('should match snapshot with default props', () => {
const {toJSON} = render(<DropdownIcon {...defaultProps} />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with different icon', () => {
const {toJSON} = render(<DropdownIcon {...defaultProps} icon="menu" />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with empty items', () => {
const {toJSON} = render(<DropdownIcon {...defaultProps} items={[]} />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with multiple items', () => {
const manyItems = [
{label: 'Action 1', onPress: jest.fn(), icon: 'edit'},
{label: 'Action 2', onPress: jest.fn(), icon: 'share'},
{label: 'Action 3', onPress: jest.fn(), icon: 'download'},
{label: 'Action 4', onPress: jest.fn(), icon: 'upload'},
];
const {toJSON} = render(
<DropdownIcon {...defaultProps} items={manyItems} />,
);
expect(toJSON()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,83 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {DualMessageOverlay} from './DualMessageOverlay';
import {Text} from 'react-native';
// Mock react-native-elements
jest.mock('react-native-elements', () => ({
Overlay: ({children}: {children: React.ReactNode}) => <>{children}</>,
Button: jest.fn(({title}) => <>{title}</>),
}));
// Mock ui components
jest.mock('./ui', () => ({
Button: jest.fn(
({title, children}: {title?: string; children?: React.ReactNode}) => (
<>{title || children}</>
),
),
Column: ({children}: {children: React.ReactNode}) => <>{children}</>,
Row: ({children}: {children: React.ReactNode}) => <>{children}</>,
Text: ({children}: {children: React.ReactNode}) => <>{children}</>,
}));
describe('DualMessageOverlay Component', () => {
const defaultProps = {
isVisible: true,
title: 'Confirm Action',
message: 'Are you sure you want to proceed?',
};
it('should match snapshot with title and message', () => {
const {toJSON} = render(<DualMessageOverlay {...defaultProps} />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with both buttons', () => {
const {toJSON} = render(
<DualMessageOverlay
{...defaultProps}
onTryAgain={jest.fn()}
onIgnore={jest.fn()}
/>,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with only try again button', () => {
const {toJSON} = render(
<DualMessageOverlay {...defaultProps} onTryAgain={jest.fn()} />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with only ignore button', () => {
const {toJSON} = render(
<DualMessageOverlay {...defaultProps} onIgnore={jest.fn()} />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with hint text', () => {
const {toJSON} = render(
<DualMessageOverlay {...defaultProps} hint="Additional information" />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with custom height', () => {
const {toJSON} = render(
<DualMessageOverlay {...defaultProps} customHeight={300} />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with children', () => {
const {toJSON} = render(
<DualMessageOverlay {...defaultProps}>
<Text>Custom content here</Text>
</DualMessageOverlay>,
);
expect(toJSON()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,96 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {EditableListItem} from './EditableListItem';
// Mock SvgImage
jest.mock('./ui/svg', () => ({
SvgImage: {
starIcon: jest.fn(() => null),
},
}));
// Mock react-native-elements
jest.mock('react-native-elements', () => ({
Icon: jest.fn(() => null),
ListItem: ({children}: {children: React.ReactNode}) => <>{children}</>,
Overlay: ({children}: {children: React.ReactNode}) => <>{children}</>,
Input: jest.fn(() => null),
}));
// Mock ui components
jest.mock('./ui', () => ({
Button: jest.fn(() => null),
Column: ({children}: {children: React.ReactNode}) => <>{children}</>,
Row: ({children}: {children: React.ReactNode}) => <>{children}</>,
Text: ({children}: {children: React.ReactNode}) => <>{children}</>,
}));
// Add mock for ListItem.Content and ListItem.Title
const ListItem = require('react-native-elements').ListItem;
ListItem.Content = ({children}: {children: React.ReactNode}) => <>{children}</>;
ListItem.Title = ({children}: {children: React.ReactNode}) => <>{children}</>;
describe('EditableListItem Component', () => {
const mockItems = [
{label: 'Email', value: 'test@example.com', testID: 'emailItem'},
{label: 'Phone', value: '1234567890', testID: 'phoneItem'},
];
const defaultProps = {
testID: 'editableItem',
title: 'Contact Information',
content: 'Edit your details',
items: mockItems,
Icon: 'edit',
onEdit: jest.fn(),
onCancel: jest.fn(),
titleColor: '#000000',
};
it('should match snapshot with default props', () => {
const {toJSON} = render(<EditableListItem {...defaultProps} />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with custom title color', () => {
const {toJSON} = render(
<EditableListItem {...defaultProps} titleColor="#FF0000" />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with progress indicator', () => {
const {toJSON} = render(
<EditableListItem {...defaultProps} progress={true} />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with error state', () => {
const {toJSON} = render(
<EditableListItem
{...defaultProps}
response="error"
errorMessage="Failed to update"
/>,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with success response', () => {
const {toJSON} = render(
<EditableListItem {...defaultProps} response="success" />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with single item', () => {
const {toJSON} = render(
<EditableListItem
{...defaultProps}
items={[{label: 'Name', value: 'John Doe', testID: 'nameItem'}]}
/>,
);
expect(toJSON()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,49 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {GlobalContextProvider} from './GlobalContextProvider';
import {Text} from 'react-native';
// Mock xstate
jest.mock('@xstate/react', () => ({
useInterpret: jest.fn(() => ({
subscribe: jest.fn(),
})),
}));
// Mock appMachine
jest.mock('../machines/app', () => ({
appMachine: {},
}));
// Mock GlobalContext
jest.mock('../shared/GlobalContext', () => ({
GlobalContext: {
Provider: ({children}: {children: React.ReactNode}) => <>{children}</>,
},
}));
// Mock commonUtil
jest.mock('../shared/commonUtil', () => ({
logState: jest.fn(),
}));
describe('GlobalContextProvider Component', () => {
it('should match snapshot with children', () => {
const {toJSON} = render(
<GlobalContextProvider>
<Text>Test Child</Text>
</GlobalContextProvider>,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with multiple children', () => {
const {toJSON} = render(
<GlobalContextProvider>
<Text>Child 1</Text>
<Text>Child 2</Text>
</GlobalContextProvider>,
);
expect(toJSON()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,60 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {HelpScreen} from './HelpScreen';
import {Text} from 'react-native';
// Mock Modal
jest.mock('./ui/Modal', () => ({
Modal: ({children}: {children: React.ReactNode}) => <>{children}</>,
}));
// Mock BannerNotificationContainer
jest.mock('./BannerNotificationContainer', () => ({
BannerNotificationContainer: jest.fn(() => null),
}));
// Mock API
jest.mock('../shared/api', () => ({
__esModule: true,
default: jest.fn(() =>
Promise.resolve({
aboutInjiUrl: 'https://docs.inji.io',
}),
),
}));
describe('HelpScreen Component', () => {
const triggerComponent = <Text>Help</Text>;
it('should match snapshot with Inji source', () => {
const {toJSON} = render(
<HelpScreen source="Inji" triggerComponent={triggerComponent} />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with BackUp source', () => {
const {toJSON} = render(
<HelpScreen source="BackUp" triggerComponent={triggerComponent} />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with keyManagement source', () => {
const {toJSON} = render(
<HelpScreen source="keyManagement" triggerComponent={triggerComponent} />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot when disabled', () => {
const {toJSON} = render(
<HelpScreen
source="Inji"
triggerComponent={triggerComponent}
isDisabled={true}
/>,
);
expect(toJSON()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,88 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {KebabPopUp} from './KebabPopUp';
import {Text} from 'react-native';
// Mock controller
jest.mock('./KebabPopUpController', () => ({
useKebabPopUp: jest.fn(() => ({
isScanning: false,
})),
}));
// Mock kebabMenuUtils
jest.mock('./kebabMenuUtils', () => ({
getKebabMenuOptions: jest.fn(() => [
{
testID: 'pinCard',
label: 'Pin Card',
onPress: jest.fn(),
icon: null,
},
{
testID: 'removeFromWallet',
label: 'Remove',
onPress: jest.fn(),
icon: null,
},
]),
}));
// Mock react-native-elements
jest.mock('react-native-elements', () => ({
Icon: jest.fn(() => null),
ListItem: ({children}: {children: React.ReactNode}) => <>{children}</>,
Overlay: ({children}: {children: React.ReactNode}) => <>{children}</>,
}));
// Mock FlatList
jest.mock('react-native-gesture-handler', () => ({
FlatList: ({renderItem, data}: any) => (
<>{data.map((item: any, index: number) => renderItem({item, index}))}</>
),
}));
describe('KebabPopUp Component', () => {
const mockService = {} as any;
const mockVcMetadata = {
id: 'test-vc',
vcLabel: 'Test VC',
};
const defaultProps = {
iconName: 'ellipsis-vertical',
vcMetadata: mockVcMetadata as any,
isVisible: true,
onDismiss: jest.fn(),
service: mockService,
vcHasImage: false,
};
it('should match snapshot with default icon', () => {
const {toJSON} = render(<KebabPopUp {...defaultProps} />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with custom icon component', () => {
const CustomIcon = <Text>Custom</Text>;
const {toJSON} = render(<KebabPopUp {...defaultProps} icon={CustomIcon} />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with custom icon color', () => {
const {toJSON} = render(
<KebabPopUp {...defaultProps} iconColor="#FF0000" />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot when not visible', () => {
const {toJSON} = render(<KebabPopUp {...defaultProps} isVisible={false} />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with VC that has image', () => {
const {toJSON} = render(<KebabPopUp {...defaultProps} vcHasImage={true} />);
expect(toJSON()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,32 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {LanguageSelector} from './LanguageSelector';
import {Text} from 'react-native';
// Mock dependencies
jest.mock('react-native-restart', () => ({
Restart: jest.fn(),
}));
jest.mock('./ui/Picker', () => ({
Picker: jest.fn(() => null),
}));
describe('LanguageSelector Component', () => {
const defaultTrigger = <Text>Select Language</Text>;
it('should match snapshot with default trigger', () => {
const {toJSON} = render(
<LanguageSelector triggerComponent={defaultTrigger} />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with custom trigger component', () => {
const customTrigger = <Text>Choose Language</Text>;
const {toJSON} = render(
<LanguageSelector triggerComponent={customTrigger} />,
);
expect(toJSON()).toMatchSnapshot();
});
});

20
components/Logo.test.tsx Normal file
View File

@@ -0,0 +1,20 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {Logo} from './Logo';
describe('Logo Component', () => {
it('should render Logo component', () => {
const {toJSON} = render(<Logo />);
expect(toJSON()).toBeTruthy();
});
it('should render Logo with width and height props', () => {
const {toJSON} = render(<Logo width={100} height={100} />);
expect(toJSON()).toBeTruthy();
});
it('should render Logo with string width and height', () => {
const {toJSON} = render(<Logo width="100%" height="50" />);
expect(toJSON()).toBeTruthy();
});
});

View File

@@ -0,0 +1,43 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {Message} from './Message';
// Mock LinearProgress
jest.mock('react-native-elements', () => ({
LinearProgress: jest.fn(() => null),
}));
// Mock Button from ui
jest.mock('./ui', () => ({
Button: jest.fn(() => null),
Centered: ({children}: {children: React.ReactNode}) => <>{children}</>,
Column: ({children}: {children: React.ReactNode}) => <>{children}</>,
Text: ({children}: {children: React.ReactNode}) => <>{children}</>,
}));
describe('Message Component', () => {
it('should match snapshot with title only', () => {
const {toJSON} = render(<Message title="Test Title" />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with message only', () => {
const {toJSON} = render(<Message message="Test Message" />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with title and message', () => {
const {toJSON} = render(<Message title="Title" message="Message" />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with hint text', () => {
const {toJSON} = render(<Message message="Test" hint="Hint text" />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with cancel button', () => {
const {toJSON} = render(<Message message="Test" onCancel={jest.fn()} />);
expect(toJSON()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,99 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {MessageOverlay, ErrorMessageOverlay} from './MessageOverlay';
import {Text} from 'react-native';
// Mock react-native-elements
jest.mock('react-native-elements', () => ({
Overlay: ({children}: {children: React.ReactNode}) => <>{children}</>,
LinearProgress: jest.fn(() => null),
}));
// Mock ui components
jest.mock('./ui', () => ({
Button: jest.fn(() => null),
Column: ({children}: {children: React.ReactNode}) => <>{children}</>,
Text: ({children}: {children: React.ReactNode}) => <>{children}</>,
}));
describe('MessageOverlay Component', () => {
const defaultProps = {
isVisible: true,
title: 'Test Title',
message: 'Test Message',
};
it('should match snapshot with title and message', () => {
const {toJSON} = render(<MessageOverlay {...defaultProps} />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with progress indicator', () => {
const {toJSON} = render(
<MessageOverlay {...defaultProps} progress={true} />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with numeric progress', () => {
const {toJSON} = render(
<MessageOverlay {...defaultProps} progress={0.75} />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with button', () => {
const {toJSON} = render(
<MessageOverlay
{...defaultProps}
buttonText="OK"
onButtonPress={jest.fn()}
/>,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with custom children', () => {
const {toJSON} = render(
<MessageOverlay {...defaultProps}>
<Text>Custom Content</Text>
</MessageOverlay>,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with hint text', () => {
const {toJSON} = render(
<MessageOverlay {...defaultProps} hint="This is a hint" />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with custom minHeight', () => {
const {toJSON} = render(
<MessageOverlay {...defaultProps} minHeight={250} />,
);
expect(toJSON()).toMatchSnapshot();
});
});
describe('ErrorMessageOverlay Component', () => {
const errorProps = {
isVisible: true,
error: 'network',
translationPath: 'errors',
onDismiss: jest.fn(),
};
it('should match snapshot with error', () => {
const {toJSON} = render(<ErrorMessageOverlay {...errorProps} />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with testID', () => {
const {toJSON} = render(
<ErrorMessageOverlay {...errorProps} testID="errorOverlay" />,
);
expect(toJSON()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,58 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {Passcode} from './Passcode';
// Mock PasscodeVerify
jest.mock('./PasscodeVerify', () => ({
PasscodeVerify: jest.fn(() => null),
}));
// Mock telemetry
jest.mock('../shared/telemetry/TelemetryUtils', () => ({
getImpressionEventData: jest.fn(),
sendImpressionEvent: jest.fn(),
}));
describe('Passcode Component', () => {
const defaultProps = {
error: '',
storedPasscode: 'hashed-passcode',
salt: 'salt-value',
onSuccess: jest.fn(),
onError: jest.fn(),
onDismiss: jest.fn(),
};
it('should match snapshot with default props', () => {
const {toJSON} = render(<Passcode {...defaultProps} />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with custom message', () => {
const {toJSON} = render(
<Passcode
{...defaultProps}
message="Please enter your 6-digit passcode"
/>,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with error message', () => {
const {toJSON} = render(
<Passcode {...defaultProps} error="Incorrect passcode. Try again." />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with both message and error', () => {
const {toJSON} = render(
<Passcode
{...defaultProps}
message="Enter passcode"
error="Authentication failed"
/>,
);
expect(toJSON()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,54 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {PasscodeVerify} from './PasscodeVerify';
// Mock PinInput
jest.mock('./PinInput', () => ({
PinInput: jest.fn(() => null),
}));
// Mock commonUtil
jest.mock('../shared/commonUtil', () => ({
hashData: jest.fn(() => Promise.resolve('hashed-value')),
}));
// Mock telemetry
jest.mock('../shared/telemetry/TelemetryUtils', () => ({
getErrorEventData: jest.fn(),
sendErrorEvent: jest.fn(),
}));
describe('PasscodeVerify Component', () => {
const defaultProps = {
passcode: 'stored-hashed-passcode',
onSuccess: jest.fn(),
onError: jest.fn(),
salt: 'test-salt',
testID: 'passcodeVerify',
};
it('should match snapshot with default props', () => {
const {toJSON} = render(<PasscodeVerify {...defaultProps} />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with different testID', () => {
const {toJSON} = render(
<PasscodeVerify {...defaultProps} testID="customPasscode" />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot without onError handler', () => {
const {passcode, onSuccess, salt, testID} = defaultProps;
const {toJSON} = render(
<PasscodeVerify
passcode={passcode}
onSuccess={onSuccess}
salt={salt}
testID={testID}
/>,
);
expect(toJSON()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,20 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import PendingIcon from './PendingIcon';
describe('PendingIcon', () => {
it('should render PendingIcon component', () => {
const {toJSON} = render(<PendingIcon />);
expect(toJSON()).toBeTruthy();
});
it('should match snapshot', () => {
const {toJSON} = render(<PendingIcon />);
expect(toJSON()).toMatchSnapshot();
});
it('should render with custom color', () => {
const {toJSON} = render(<PendingIcon color="#FF0000" />);
expect(toJSON()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,54 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {PinInput} from './PinInput';
// Mock usePinInput
jest.mock('../machines/pinInput', () => ({
usePinInput: jest.fn(length => ({
state: {
context: {
inputRefs: Array(length).fill({current: null}),
values: Array(length).fill(''),
},
},
send: jest.fn(),
events: {
UPDATE_INPUT: jest.fn((value, index) => ({
type: 'UPDATE_INPUT',
value,
index,
})),
FOCUS_INPUT: jest.fn(index => ({type: 'FOCUS_INPUT', index})),
KEY_PRESS: jest.fn(key => ({type: 'KEY_PRESS', key})),
},
})),
}));
describe('PinInput Component', () => {
it('should match snapshot with 4 digit PIN', () => {
const {toJSON} = render(<PinInput length={4} />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with 6 digit PIN', () => {
const {toJSON} = render(<PinInput length={6} />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with onChange handler', () => {
const {toJSON} = render(<PinInput length={4} onChange={jest.fn()} />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with onDone and autosubmit', () => {
const {toJSON} = render(
<PinInput length={4} onDone={jest.fn()} autosubmit={true} />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with custom testID', () => {
const {toJSON} = render(<PinInput length={4} testID="customPinInput" />);
expect(toJSON()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,50 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {ProfileIcon} from './ProfileIcon';
import {View} from 'react-native';
// Mock SvgImage
jest.mock('./ui/svg', () => ({
SvgImage: {
pinIcon: jest.fn(() => <View testID="mockPinIcon" />),
},
}));
describe('ProfileIcon Component', () => {
const defaultProps = {
profileIconContainerStyles: {},
profileIconSize: 40,
};
it('should match snapshot without pinned icon', () => {
const {toJSON} = render(<ProfileIcon {...defaultProps} />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with pinned icon', () => {
const {toJSON} = render(<ProfileIcon {...defaultProps} isPinned={true} />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with custom icon size', () => {
const {toJSON} = render(
<ProfileIcon {...defaultProps} profileIconSize={60} />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with custom container styles', () => {
const customStyles = {
backgroundColor: 'blue',
borderRadius: 10,
padding: 5,
};
const {toJSON} = render(
<ProfileIcon
{...defaultProps}
profileIconContainerStyles={customStyles}
/>,
);
expect(toJSON()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,100 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {ProgressingModal} from './ProgressingModal';
// Mock Modal
jest.mock('./ui/Modal', () => ({
Modal: ({children}: {children: React.ReactNode}) => <>{children}</>,
}));
// Mock Spinner
jest.mock('react-native-spinkit', () => 'Spinner');
// Mock SvgImage
jest.mock('./ui/svg', () => ({
SvgImage: {
ProgressIcon: jest.fn(() => null),
},
}));
// Mock ui components
jest.mock('./ui', () => ({
Button: jest.fn(() => null),
Centered: ({children}: {children: React.ReactNode}) => <>{children}</>,
Column: ({children}: {children: React.ReactNode}) => <>{children}</>,
Text: ({children}: {children: React.ReactNode}) => <>{children}</>,
}));
describe('ProgressingModal Component', () => {
const defaultProps = {
isVisible: true,
isHintVisible: false,
title: 'Processing',
};
it('should match snapshot with default props', () => {
const {toJSON} = render(<ProgressingModal {...defaultProps} />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with progress spinner', () => {
const {toJSON} = render(
<ProgressingModal {...defaultProps} progress={true} />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with hint visible', () => {
const {toJSON} = render(
<ProgressingModal
{...defaultProps}
isHintVisible={true}
hint="Please wait..."
/>,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with retry button', () => {
const {toJSON} = render(
<ProgressingModal
{...defaultProps}
isHintVisible={true}
hint="Connection failed"
onRetry={jest.fn()}
/>,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with stay in progress button', () => {
const {toJSON} = render(
<ProgressingModal
{...defaultProps}
isHintVisible={true}
hint="Taking longer than expected"
onStayInProgress={jest.fn()}
/>,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with BLE error visible', () => {
const {toJSON} = render(
<ProgressingModal
{...defaultProps}
isHintVisible={true}
isBleErrorVisible={true}
hint="Bluetooth error occurred"
/>,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot as requester', () => {
const {toJSON} = render(
<ProgressingModal {...defaultProps} requester={true} />,
);
expect(toJSON()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,94 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {QrCodeOverlay} from './QrCodeOverlay';
import {NativeModules} from 'react-native';
// Mock QRCode
jest.mock('react-native-qrcode-svg', () => 'QRCode');
// Mock SvgImage
jest.mock('./ui/svg', () => ({
SvgImage: {
MagnifierZoom: jest.fn(() => null),
},
}));
// Mock sharing utils
jest.mock('../shared/sharing/imageUtils', () => ({
shareImageToAllSupportedApps: jest.fn(() => Promise.resolve(true)),
}));
describe('QrCodeOverlay Component', () => {
// Setup mocks for native modules
beforeAll(() => {
// Mock RNSecureKeystoreModule methods
NativeModules.RNSecureKeystoreModule.getData = jest.fn(() =>
Promise.resolve(['key', 'mocked-qr-data']),
);
NativeModules.RNSecureKeystoreModule.storeData = jest.fn(() =>
Promise.resolve(),
);
// Mock RNPixelpassModule
NativeModules.RNPixelpassModule = {
generateQRData: jest.fn(() => Promise.resolve('mocked-qr-data')),
};
});
// Silence console warnings during tests
beforeAll(() => {
jest.spyOn(console, 'warn').mockImplementation();
jest.spyOn(console, 'error').mockImplementation();
});
afterAll(() => {
jest.restoreAllMocks();
});
const mockVC = {
credential: {id: 'test-credential'},
generatedOn: new Date().toISOString(),
};
const mockMeta = {
id: 'test-vc-id',
vcLabel: 'Test VC',
};
const defaultProps = {
verifiableCredential: mockVC as any,
meta: mockMeta as any,
};
// NOTE: CodeRabbit suggested making these tests async to wait for QR data loading.
// However, the component requires native module mocks (RNSecureKeystoreModule.getData)
// that are not properly initialized in the test environment, causing the component
// to always return null. These tests currently capture empty snapshots.
// TODO: Fix native module mocking to properly test the async QR data loading behavior.
it('should match snapshot with default props', () => {
const {toJSON} = render(<QrCodeOverlay {...defaultProps} />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with inline QR disabled', () => {
const {toJSON} = render(
<QrCodeOverlay {...defaultProps} showInlineQr={false} />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with force visible', () => {
const {toJSON} = render(
<QrCodeOverlay {...defaultProps} forceVisible={true} />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with onClose handler', () => {
const {toJSON} = render(
<QrCodeOverlay {...defaultProps} onClose={jest.fn()} />,
);
expect(toJSON()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,106 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {QrScanner} from './QrScanner';
// Mock useContext
const mockUseContext = jest.fn();
jest.spyOn(React, 'useContext').mockImplementation(mockUseContext);
// Mock GlobalContext
jest.mock('../shared/GlobalContext', () => ({
GlobalContext: {},
}));
// Mock xstate with a mutable mock function
const mockUseSelector = jest.fn();
jest.mock('@xstate/react', () => ({
useSelector: jest.fn((...args) => mockUseSelector(...args)),
}));
// Before each test, set up the context mock
beforeEach(() => {
mockUseContext.mockReturnValue({
appService: {send: jest.fn()},
});
mockUseSelector.mockReturnValue(true);
});
// Mock app machine
jest.mock('../machines/app', () => ({
selectIsActive: jest.fn(),
}));
// Mock SvgImage
jest.mock('./ui/svg', () => ({
SvgImage: {
FlipCameraIcon: jest.fn(() => null),
},
}));
// Mock ui components
jest.mock('./ui', () => ({
Column: ({children}: {children: React.ReactNode}) => <>{children}</>,
Row: ({children}: {children: React.ReactNode}) => <>{children}</>,
Text: ({children}: {children: React.ReactNode}) => <>{children}</>,
}));
// Mock expo-camera
jest.mock('expo-camera', () => ({
CameraView: jest.fn(() => null),
useCameraPermissions: jest.fn(() => [
null,
jest.fn(() => Promise.resolve({granted: true})),
jest.fn(() => Promise.resolve({status: 'granted'})),
]),
PermissionStatus: {
UNDETERMINED: 'undetermined',
GRANTED: 'granted',
DENIED: 'denied',
},
CameraType: {
BACK: 'back',
FRONT: 'front',
},
}));
describe('QrScanner Component', () => {
const defaultProps = {
onQrFound: jest.fn(),
};
beforeEach(() => {
jest.clearAllMocks();
});
it('should match snapshot with default props', () => {
const {toJSON} = render(<QrScanner {...defaultProps} />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with title', () => {
const {toJSON} = render(
<QrScanner {...defaultProps} title="Hold phone steady to scan QR code" />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with custom title', () => {
const {toJSON} = render(
<QrScanner {...defaultProps} title="Scan QR code to share credentials" />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should render with onQrFound callback', () => {
const onQrFound = jest.fn();
const {toJSON} = render(<QrScanner onQrFound={onQrFound} />);
expect(toJSON()).toBeTruthy();
expect(onQrFound).not.toHaveBeenCalled(); // Callback not called until QR is scanned
});
it('should render when isActive is false', () => {
mockUseSelector.mockReturnValue(false);
const {toJSON} = render(<QrScanner {...defaultProps} />);
expect(toJSON()).toBeTruthy();
});
});

View File

@@ -0,0 +1,99 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {RotatingIcon} from './RotatingIcon';
describe('RotatingIcon Component', () => {
it('should render RotatingIcon component', () => {
const {toJSON} = render(
<RotatingIcon name="sync" size={24} color="#000000" clockwise={true} />,
);
expect(toJSON()).toBeTruthy();
});
it('should render with clockwise rotation', () => {
const {toJSON} = render(
<RotatingIcon
name="refresh"
size={30}
color="#FF0000"
clockwise={true}
/>,
);
expect(toJSON()).toBeTruthy();
});
it('should render with counter-clockwise rotation', () => {
const {toJSON} = render(
<RotatingIcon
name="refresh"
size={30}
color="#0000FF"
clockwise={false}
/>,
);
expect(toJSON()).toBeTruthy();
});
it('should render with custom duration', () => {
const {toJSON} = render(
<RotatingIcon
name="sync"
size={24}
color="#00FF00"
clockwise={true}
duration={5000}
/>,
);
expect(toJSON()).toBeTruthy();
});
it('should render with default duration', () => {
const {toJSON} = render(
<RotatingIcon
name="loading"
size={20}
color="#CCCCCC"
clockwise={true}
/>,
);
expect(toJSON()).toBeTruthy();
});
it('should handle different icon names', () => {
const iconNames = ['sync', 'refresh', 'loading', 'autorenew'];
iconNames.forEach(name => {
const {toJSON} = render(
<RotatingIcon name={name} size={24} color="#000000" clockwise={true} />,
);
expect(toJSON()).toBeTruthy();
});
});
it('should handle different sizes', () => {
const sizes = [16, 24, 32, 48];
sizes.forEach(size => {
const {toJSON} = render(
<RotatingIcon
name="sync"
size={size}
color="#000000"
clockwise={true}
/>,
);
expect(toJSON()).toBeTruthy();
});
});
it('should handle different colors', () => {
const colors = ['#FF0000', '#00FF00', '#0000FF', '#FFFFFF'];
colors.forEach(color => {
const {toJSON} = render(
<RotatingIcon name="sync" size={24} color={color} clockwise={true} />,
);
expect(toJSON()).toBeTruthy();
});
});
});

View File

@@ -0,0 +1,70 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {SectionLayout} from './SectionLayout';
import {Text, View} from 'react-native';
// Mock ui components
jest.mock('./ui', () => ({
Column: ({children}: {children: React.ReactNode}) => <>{children}</>,
Row: ({children}: {children: React.ReactNode}) => <>{children}</>,
Text: ({children}: {children: React.ReactNode}) => <>{children}</>,
}));
describe('SectionLayout Component', () => {
const defaultProps = {
headerIcon: <View testID="headerIcon">Icon</View>,
headerText: 'Section Header',
children: <Text>Section Content</Text>,
testId: 'testSection',
};
it('should match snapshot with default props', () => {
const {toJSON} = render(<SectionLayout {...defaultProps} />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with custom marginBottom', () => {
const {toJSON} = render(
<SectionLayout {...defaultProps} marginBottom={20} />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with different header text', () => {
const {toJSON} = render(
<SectionLayout {...defaultProps} headerText="Custom Section Title" />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with complex children', () => {
const {toJSON} = render(
<SectionLayout
headerIcon={defaultProps.headerIcon}
headerText={defaultProps.headerText}
testId={defaultProps.testId}>
<Text>Line 1</Text>
<Text>Line 2</Text>
<View>
<Text>Nested content</Text>
</View>
</SectionLayout>,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with different icon', () => {
const customIcon = <View testID="customIcon">🔍</View>;
const {toJSON} = render(
<SectionLayout {...defaultProps} headerIcon={customIcon} />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with zero marginBottom', () => {
const {toJSON} = render(
<SectionLayout {...defaultProps} marginBottom={0} />,
);
expect(toJSON()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,150 @@
import React from 'react';
import {render, fireEvent} from '@testing-library/react-native';
import {TextEditOverlay} from './TextEditOverlay';
// Mock react-native-elements with a more realistic Input
jest.mock('react-native-elements', () => {
const RN = jest.requireActual('react-native');
return {
Input: (props: {value: string; onChangeText: (text: string) => void}) => (
<RN.TextInput
value={props.value}
onChangeText={props.onChangeText}
testID="text-input"
/>
),
};
});
// Mock ui components with more realistic Button
jest.mock('./ui', () => {
const RN = jest.requireActual('react-native');
const React = jest.requireActual('react');
return {
Button: (props: {title: string; onPress: () => void}) => (
<RN.TouchableOpacity
onPress={props.onPress}
testID={`button-${props.title}`}>
<RN.Text>{props.title}</RN.Text>
</RN.TouchableOpacity>
),
Centered: ({children}: {children: React.ReactNode}) => <>{children}</>,
Column: ({children}: {children: React.ReactNode}) => <>{children}</>,
Row: ({children}: {children: React.ReactNode}) => <>{children}</>,
Text: ({children}: {children: React.ReactNode}) => <>{children}</>,
};
});
describe('TextEditOverlay Component', () => {
const defaultProps = {
isVisible: true,
label: 'Edit Name',
value: 'John Doe',
onSave: jest.fn(),
onDismiss: jest.fn(),
};
beforeEach(() => {
jest.clearAllMocks();
});
it('should match snapshot with default props', () => {
const {toJSON} = render(<TextEditOverlay {...defaultProps} />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with different label', () => {
const {toJSON} = render(
<TextEditOverlay {...defaultProps} label="Edit Email" />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with maxLength', () => {
const {toJSON} = render(
<TextEditOverlay {...defaultProps} maxLength={50} />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with empty value', () => {
const {toJSON} = render(<TextEditOverlay {...defaultProps} value="" />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with long value', () => {
const {toJSON} = render(
<TextEditOverlay
{...defaultProps}
value="This is a very long text value that should be editable"
/>,
);
expect(toJSON()).toMatchSnapshot();
});
it('should call onSave with value when save button is pressed', () => {
const onSave = jest.fn();
const {getByTestId} = render(
<TextEditOverlay {...defaultProps} onSave={onSave} value="Test Value" />,
);
const saveButton = getByTestId('button-save');
fireEvent.press(saveButton);
// onSave should be called with the current value
expect(onSave).toHaveBeenCalledTimes(1);
expect(typeof onSave.mock.calls[0][0]).toBe('string');
});
it('should call onSave with original value when save is pressed without changes', () => {
const onSave = jest.fn();
const {getByTestId} = render(
<TextEditOverlay {...defaultProps} onSave={onSave} />,
);
const saveButton = getByTestId('button-save');
fireEvent.press(saveButton);
expect(onSave).toHaveBeenCalledWith('John Doe');
});
it('should call onDismiss and reset value when cancel button is pressed', () => {
const onDismiss = jest.fn();
const {getByTestId} = render(
<TextEditOverlay {...defaultProps} onDismiss={onDismiss} />,
);
const input = getByTestId('text-input');
// Simulate text change
input.props.onChangeText('Modified Text');
const cancelButton = getByTestId('button-cancel');
fireEvent.press(cancelButton);
expect(onDismiss).toHaveBeenCalled();
});
it('should handle text input changes', () => {
const {getByTestId} = render(<TextEditOverlay {...defaultProps} />);
const input = getByTestId('text-input');
// Verify input has onChangeText handler
expect(input.props.onChangeText).toBeDefined();
expect(typeof input.props.onChangeText).toBe('function');
});
it('should not use isVisible prop', () => {
// Note: isVisible is defined in the interface but not used in the component
// This test documents that the prop exists but has no effect
const {toJSON: jsonVisible} = render(
<TextEditOverlay {...defaultProps} isVisible={true} />,
);
const {toJSON: jsonHidden} = render(
<TextEditOverlay {...defaultProps} isVisible={false} />,
);
// Both render the same output regardless of isVisible value
expect(JSON.stringify(jsonVisible())).toEqual(JSON.stringify(jsonHidden()));
});
});

View File

@@ -0,0 +1,105 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {TrustModal} from './TrustModal';
// Mock useTranslation hook
const mockT = jest.fn((key: string) => {
if (key === 'infoPoints' || key === 'verifierInfoPoints') {
return ['Point 1', 'Point 2', 'Point 3'];
}
return key;
});
jest.mock('react-i18next', () => ({
...jest.requireActual('react-i18next'),
useTranslation: () => ({
t: mockT,
i18n: {changeLanguage: jest.fn()},
}),
}));
// Mock ui components
jest.mock('./ui', () => ({
Button: jest.fn(() => null),
Column: ({children}: {children: React.ReactNode}) => <>{children}</>,
Row: ({children}: {children: React.ReactNode}) => <>{children}</>,
Text: ({children}: {children: React.ReactNode}) => <>{children}</>,
}));
// Mock react-native components
jest.mock('react-native', () => {
const ReactNative = jest.requireActual('react-native');
return {
...ReactNative,
Modal: ({children}: {children: React.ReactNode}) => <>{children}</>,
View: ({children}: {children: React.ReactNode}) => <>{children}</>,
ScrollView: ({children}: {children: React.ReactNode}) => <>{children}</>,
Image: jest.fn(() => null),
};
});
describe('TrustModal Component', () => {
const defaultProps = {
isVisible: true,
logo: 'https://example.com/logo.png',
name: 'Test Issuer',
onConfirm: jest.fn(),
onCancel: jest.fn(),
};
it('should match snapshot with issuer flow', () => {
const {toJSON} = render(<TrustModal {...defaultProps} flowType="issuer" />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with verifier flow', () => {
const {toJSON} = render(
<TrustModal {...defaultProps} flowType="verifier" />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot when not visible', () => {
const {toJSON} = render(<TrustModal {...defaultProps} isVisible={false} />);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot without logo', () => {
const {toJSON} = render(
<TrustModal {...defaultProps} logo={undefined} flowType="issuer" />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot without name', () => {
const {toJSON} = render(
<TrustModal {...defaultProps} name={''} flowType="issuer" />,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot without logo and name', () => {
const {toJSON} = render(
<TrustModal
isVisible={true}
logo={undefined}
name={''}
onConfirm={jest.fn()}
onCancel={jest.fn()}
flowType="issuer"
/>,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with long name', () => {
const {toJSON} = render(
<TrustModal
{...defaultProps}
name="Very Long Issuer Name That Should Wrap Properly"
flowType="verifier"
/>,
);
expect(toJSON()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,75 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {VCVerification} from './VCVerification';
import {VCMetadata} from '../shared/VCMetadata';
import {Display} from './VC/common/VCUtils';
// Mock the Display class
const mockDisplay = {
getTextColor: jest.fn((defaultColor: string) => defaultColor),
} as unknown as Display;
describe('VCVerification Component', () => {
it('should render for verified and valid credential', () => {
const vcMetadata = new VCMetadata({
isVerified: true,
isExpired: false,
});
const {toJSON} = render(
<VCVerification vcMetadata={vcMetadata} display={mockDisplay} />,
);
expect(toJSON()).toBeTruthy();
});
it('should render for verified but expired credential', () => {
const vcMetadata = new VCMetadata({
isVerified: true,
isExpired: true,
});
const {toJSON} = render(
<VCVerification vcMetadata={vcMetadata} display={mockDisplay} />,
);
expect(toJSON()).toBeTruthy();
});
it('should render for pending/unverified credential', () => {
const vcMetadata = new VCMetadata({
isVerified: false,
isExpired: false,
});
const {toJSON} = render(
<VCVerification vcMetadata={vcMetadata} display={mockDisplay} />,
);
expect(toJSON()).toBeTruthy();
});
it('should render verification status text', () => {
const vcMetadata = new VCMetadata({
isVerified: true,
isExpired: false,
});
const {getByText} = render(
<VCVerification vcMetadata={vcMetadata} display={mockDisplay} />,
);
expect(getByText('valid')).toBeTruthy();
});
it('should call getTextColor from display prop', () => {
const vcMetadata = new VCMetadata({
isVerified: true,
isExpired: false,
});
render(<VCVerification vcMetadata={vcMetadata} display={mockDisplay} />);
expect(mockDisplay.getTextColor).toHaveBeenCalled();
});
});

View File

@@ -0,0 +1,234 @@
import {VPShareActivityLog} from './VPShareActivityLogEvent';
import {VCItemContainerFlowType} from '../shared/Utils';
describe('VPShareActivityLog', () => {
describe('constructor', () => {
it('should create instance with default values', () => {
const log = new VPShareActivityLog({});
expect(log.type).toBe('');
expect(log.timestamp).toBeDefined();
expect(log.flow).toBe(VCItemContainerFlowType.VP_SHARE);
expect(log.info).toBe('');
});
it('should create instance with provided values', () => {
const timestamp = Date.now();
const log = new VPShareActivityLog({
type: 'SHARED_SUCCESSFULLY',
timestamp,
flow: VCItemContainerFlowType.QR_LOGIN,
info: 'Test info',
});
expect(log.type).toBe('SHARED_SUCCESSFULLY');
expect(log.timestamp).toBe(timestamp);
expect(log.flow).toBe(VCItemContainerFlowType.QR_LOGIN);
expect(log.info).toBe('Test info');
});
it('should handle different activity types', () => {
const types: Array<
| 'SHARED_SUCCESSFULLY'
| 'SHARED_WITH_FACE_VERIFIACTION'
| 'VERIFIER_AUTHENTICATION_FAILED'
| 'INVALID_AUTH_REQUEST'
| 'USER_DECLINED_CONSENT'
> = [
'SHARED_SUCCESSFULLY',
'SHARED_WITH_FACE_VERIFIACTION',
'VERIFIER_AUTHENTICATION_FAILED',
'INVALID_AUTH_REQUEST',
'USER_DECLINED_CONSENT',
];
types.forEach(type => {
const log = new VPShareActivityLog({type});
expect(log.type).toBe(type);
});
});
});
describe('getActionText', () => {
it('should return formatted action text', () => {
const log = new VPShareActivityLog({
type: 'SHARED_SUCCESSFULLY',
info: 'Test info',
});
const mockT = jest.fn(key => `Translated: ${key}`);
const result = log.getActionText(mockT);
expect(mockT).toHaveBeenCalledWith(
'ActivityLogText:vpSharing:SHARED_SUCCESSFULLY',
{info: 'Test info'},
);
expect(result).toContain('Translated:');
});
it('should handle empty type', () => {
const log = new VPShareActivityLog({type: ''});
const mockT = jest.fn(key => key);
log.getActionText(mockT);
expect(mockT).toHaveBeenCalled();
});
it('should pass info to translation function', () => {
const log = new VPShareActivityLog({
type: 'TECHNICAL_ERROR',
info: 'Error details',
});
const mockT = jest.fn();
log.getActionText(mockT);
expect(mockT).toHaveBeenCalledWith(
expect.any(String),
expect.objectContaining({info: 'Error details'}),
);
});
});
describe('getLogFromObject', () => {
it('should create log from object', () => {
const data = {
type: 'SHARED_SUCCESSFULLY',
timestamp: 1234567890,
flow: VCItemContainerFlowType.VP_SHARE,
info: 'Test',
};
const log = VPShareActivityLog.getLogFromObject(data);
expect(log).toBeInstanceOf(VPShareActivityLog);
expect(log.type).toBe('SHARED_SUCCESSFULLY');
expect(log.timestamp).toBe(1234567890);
});
it('should handle empty object', () => {
const log = VPShareActivityLog.getLogFromObject({});
expect(log).toBeInstanceOf(VPShareActivityLog);
expect(log.type).toBe('');
});
it('should handle partial object', () => {
const log = VPShareActivityLog.getLogFromObject({
type: 'USER_DECLINED_CONSENT',
});
expect(log).toBeInstanceOf(VPShareActivityLog);
expect(log.type).toBe('USER_DECLINED_CONSENT');
});
});
describe('getActionLabel', () => {
it('should return formatted action label in English', () => {
const log = new VPShareActivityLog({
timestamp: Date.now() - 60000, // 1 minute ago
});
const label = log.getActionLabel('enUS');
expect(label).toBeDefined();
expect(typeof label).toBe('string');
expect(label.length).toBeGreaterThan(0);
});
it('should handle different languages', () => {
const log = new VPShareActivityLog({
timestamp: Date.now() - 3600000, // 1 hour ago
});
const languages = ['enUS', 'hi', 'kn', 'ta', 'ar'];
languages.forEach(lang => {
const label = log.getActionLabel(lang);
expect(label).toBeDefined();
expect(typeof label).toBe('string');
});
});
it('should show relative time', () => {
const recentTimestamp = Date.now() - 5000; // 5 seconds ago
const log = new VPShareActivityLog({timestamp: recentTimestamp});
const label = log.getActionLabel('enUS');
expect(label).toBeDefined();
expect(label.length).toBeGreaterThan(0);
});
it('should handle old timestamps', () => {
const oldTimestamp = Date.now() - 86400000; // 1 day ago
const log = new VPShareActivityLog({timestamp: oldTimestamp});
const label = log.getActionLabel('enUS');
expect(label).toBeDefined();
expect(label).toContain('ago');
});
});
describe('Activity Log Types', () => {
it('should handle all success types', () => {
const successTypes: Array<
| 'SHARED_SUCCESSFULLY'
| 'SHARED_WITH_FACE_VERIFIACTION'
| 'SHARED_AFTER_RETRY'
| 'SHARED_WITH_FACE_VERIFICATION_AFTER_RETRY'
> = [
'SHARED_SUCCESSFULLY',
'SHARED_WITH_FACE_VERIFIACTION',
'SHARED_AFTER_RETRY',
'SHARED_WITH_FACE_VERIFICATION_AFTER_RETRY',
];
successTypes.forEach(type => {
const log = new VPShareActivityLog({type});
expect(log.type).toBe(type);
});
});
it('should handle all error types', () => {
const errorTypes: Array<
| 'VERIFIER_AUTHENTICATION_FAILED'
| 'INVALID_AUTH_REQUEST'
| 'RETRY_ATTEMPT_FAILED'
| 'MAX_RETRY_ATTEMPT_FAILED'
| 'FACE_VERIFICATION_FAILED'
| 'TECHNICAL_ERROR'
> = [
'VERIFIER_AUTHENTICATION_FAILED',
'INVALID_AUTH_REQUEST',
'RETRY_ATTEMPT_FAILED',
'MAX_RETRY_ATTEMPT_FAILED',
'FACE_VERIFICATION_FAILED',
'TECHNICAL_ERROR',
];
errorTypes.forEach(type => {
const log = new VPShareActivityLog({type});
expect(log.type).toBe(type);
});
});
it('should handle credential-related types', () => {
const credentialTypes: Array<
| 'NO_SELECTED_VC_HAS_IMAGE'
| 'CREDENTIAL_MISMATCH_FROM_KEBAB'
| 'NO_CREDENTIAL_MATCHING_REQUEST'
> = [
'NO_SELECTED_VC_HAS_IMAGE',
'CREDENTIAL_MISMATCH_FROM_KEBAB',
'NO_CREDENTIAL_MATCHING_REQUEST',
];
credentialTypes.forEach(type => {
const log = new VPShareActivityLog({type});
expect(log.type).toBe(type);
});
});
});
});

View File

@@ -0,0 +1,71 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {VcItemContainerProfileImage} from './VcItemContainerProfileImage';
import {View} from 'react-native';
// Mock SvgImage
jest.mock('./ui/svg', () => ({
SvgImage: {
pinIcon: jest.fn(() => <View testID="mockPinIcon" />),
},
}));
// Mock ProfileIcon
jest.mock('./ProfileIcon', () => ({
ProfileIcon: jest.fn(() => <View testID="mockProfileIcon" />),
}));
describe('VcItemContainerProfileImage Component', () => {
const vcDataWithImage = {
face: 'https://example.com/avatar.jpg',
};
const vcDataWithoutImage = {
face: null,
};
it('should match snapshot with face image', () => {
const {toJSON} = render(
<VcItemContainerProfileImage
verifiableCredentialData={vcDataWithImage}
/>,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with face image and pinned', () => {
const {toJSON} = render(
<VcItemContainerProfileImage
verifiableCredentialData={vcDataWithImage}
isPinned={true}
/>,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot without face image', () => {
const {toJSON} = render(
<VcItemContainerProfileImage
verifiableCredentialData={vcDataWithoutImage}
/>,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot without face image and pinned', () => {
const {toJSON} = render(
<VcItemContainerProfileImage
verifiableCredentialData={vcDataWithoutImage}
isPinned={true}
/>,
);
expect(toJSON()).toMatchSnapshot();
});
it('should match snapshot with empty string face', () => {
const {toJSON} = render(
<VcItemContainerProfileImage verifiableCredentialData={{face: ''}} />,
);
expect(toJSON()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,30 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import VerifiedIcon from './VerifiedIcon';
describe('VerifiedIcon', () => {
it('should render VerifiedIcon component', () => {
const {toJSON} = render(<VerifiedIcon />);
expect(toJSON()).toBeTruthy();
});
it('should match snapshot', () => {
const {toJSON} = render(<VerifiedIcon />);
expect(toJSON()).toMatchSnapshot();
});
it('should have proper styling structure', () => {
const {toJSON} = render(<VerifiedIcon />);
const tree = toJSON();
// Verify component structure exists
expect(tree).toBeTruthy();
expect(tree.children).toBeTruthy();
});
it('should render with check-circle icon', () => {
const {toJSON} = render(<VerifiedIcon />);
const tree = toJSON();
expect(tree).toBeTruthy();
});
});

View File

@@ -0,0 +1,821 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AccountInformation Component should match snapshot with different email 1`] = `
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "row",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
{
"columnGap": 11,
"marginBottom": 21,
},
null,
null,
]
}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
{
"justifyContent": "center",
},
null,
null,
null,
null,
null,
]
}
>
<Image
accessibilityLabel="associatedAccountPicture"
accessible={true}
source={
{
"uri": "https://example.com/avatar.jpg",
}
}
style={
{
"borderRadius": 45,
"height": 40,
"width": 40,
}
}
/>
</View>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "row",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<Text
accessibilityLabel="associatedAccount"
style={
[
{
"color": "#000000",
"fontSize": 16,
"lineHeight": 18,
},
{
"fontFamily": "Inter_400Regular",
"fontSize": 14,
},
null,
{
"textAlign": "left",
},
null,
null,
{
"color": "#707070",
"fontSize": 12,
},
]
}
>
associatedAccount
</Text>
</View>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "row",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<Text
accessibilityLabel="associatedAccountEmail"
style={
[
{
"color": "#000000",
"fontSize": 16,
"lineHeight": 18,
},
{
"fontFamily": "Inter_400Regular",
"fontSize": 14,
},
null,
{
"textAlign": "left",
},
null,
null,
{
"fontFamily": "Helvetica Neue",
"fontSize": 13,
},
]
}
>
another@test.com
</Text>
</View>
</View>
</View>
`;
exports[`AccountInformation Component should match snapshot with different picture URL 1`] = `
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "row",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
{
"columnGap": 11,
"marginBottom": 21,
},
null,
null,
]
}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
{
"justifyContent": "center",
},
null,
null,
null,
null,
null,
]
}
>
<Image
accessibilityLabel="associatedAccountPicture"
accessible={true}
source={
{
"uri": "https://example.com/different-avatar.jpg",
}
}
style={
{
"borderRadius": 45,
"height": 40,
"width": 40,
}
}
/>
</View>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "row",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<Text
accessibilityLabel="associatedAccount"
style={
[
{
"color": "#000000",
"fontSize": 16,
"lineHeight": 18,
},
{
"fontFamily": "Inter_400Regular",
"fontSize": 14,
},
null,
{
"textAlign": "left",
},
null,
null,
{
"color": "#707070",
"fontSize": 12,
},
]
}
>
associatedAccount
</Text>
</View>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "row",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<Text
accessibilityLabel="associatedAccountEmail"
style={
[
{
"color": "#000000",
"fontSize": 16,
"lineHeight": 18,
},
{
"fontFamily": "Inter_400Regular",
"fontSize": 14,
},
null,
{
"textAlign": "left",
},
null,
null,
{
"fontFamily": "Helvetica Neue",
"fontSize": 13,
},
]
}
>
test@example.com
</Text>
</View>
</View>
</View>
`;
exports[`AccountInformation Component should match snapshot with email and picture 1`] = `
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "row",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
{
"columnGap": 11,
"marginBottom": 21,
},
null,
null,
]
}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
{
"justifyContent": "center",
},
null,
null,
null,
null,
null,
]
}
>
<Image
accessibilityLabel="associatedAccountPicture"
accessible={true}
source={
{
"uri": "https://example.com/avatar.jpg",
}
}
style={
{
"borderRadius": 45,
"height": 40,
"width": 40,
}
}
/>
</View>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "row",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<Text
accessibilityLabel="associatedAccount"
style={
[
{
"color": "#000000",
"fontSize": 16,
"lineHeight": 18,
},
{
"fontFamily": "Inter_400Regular",
"fontSize": 14,
},
null,
{
"textAlign": "left",
},
null,
null,
{
"color": "#707070",
"fontSize": 12,
},
]
}
>
associatedAccount
</Text>
</View>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "row",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<Text
accessibilityLabel="associatedAccountEmail"
style={
[
{
"color": "#000000",
"fontSize": 16,
"lineHeight": 18,
},
{
"fontFamily": "Inter_400Regular",
"fontSize": 14,
},
null,
{
"textAlign": "left",
},
null,
null,
{
"fontFamily": "Helvetica Neue",
"fontSize": 13,
},
]
}
>
test@example.com
</Text>
</View>
</View>
</View>
`;
exports[`AccountInformation Component should match snapshot with long email 1`] = `
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "row",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
{
"columnGap": 11,
"marginBottom": 21,
},
null,
null,
]
}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
{
"justifyContent": "center",
},
null,
null,
null,
null,
null,
]
}
>
<Image
accessibilityLabel="associatedAccountPicture"
accessible={true}
source={
{
"uri": "https://example.com/avatar.jpg",
}
}
style={
{
"borderRadius": 45,
"height": 40,
"width": 40,
}
}
/>
</View>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "row",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<Text
accessibilityLabel="associatedAccount"
style={
[
{
"color": "#000000",
"fontSize": 16,
"lineHeight": 18,
},
{
"fontFamily": "Inter_400Regular",
"fontSize": 14,
},
null,
{
"textAlign": "left",
},
null,
null,
{
"color": "#707070",
"fontSize": 12,
},
]
}
>
associatedAccount
</Text>
</View>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "row",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<Text
accessibilityLabel="associatedAccountEmail"
style={
[
{
"color": "#000000",
"fontSize": 16,
"lineHeight": 18,
},
{
"fontFamily": "Inter_400Regular",
"fontSize": 14,
},
null,
{
"textAlign": "left",
},
null,
null,
{
"fontFamily": "Helvetica Neue",
"fontSize": 13,
},
]
}
>
very.long.email.address@example-domain.com
</Text>
</View>
</View>
</View>
`;

View File

@@ -0,0 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ActivityLogText Component should match snapshot with VC activity 1`] = `null`;
exports[`ActivityLogText Component should match snapshot with VP activity 1`] = `null`;

View File

@@ -0,0 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`BackupAndRestoreBannerNotification Component should match snapshot with no banners 1`] = `null`;

View File

@@ -0,0 +1,851 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`BannerNotification Component should match snapshot with different testId 1`] = `
<View
accessibilityLabel="customBanner"
accessible={true}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "row",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
[
{
"alignItems": "flex-start",
"backgroundColor": "#DB2E2E",
"columnGap": 7,
"justifyContent": "space-between",
"marginVertical": 1,
"paddingHorizontal": 18,
"paddingVertical": 12,
"position": "relative",
"width": "100%",
},
{
"backgroundColor": "#4B9D20",
},
],
null,
null,
]
}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
{
"flex": 1,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<Text
accessibilityLabel="customBannerText"
style={
[
{
"color": "#000000",
"fontSize": 16,
"lineHeight": 18,
},
{
"fontFamily": "Inter_600SemiBold",
"fontSize": 15,
},
{
"color": "#FFFFFF",
},
{
"textAlign": "left",
},
null,
null,
{
"fontFamily": "Inter_600SemiBold",
"fontSize": 12,
"lineHeight": 15,
"padding": 1,
"textAlignVertical": "center",
},
]
}
>
Test notification message
</Text>
</View>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<View
accessibilityLabel="close"
accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
{
"paddingLeft": 9,
}
}
/>
</View>
</View>
</View>
`;
exports[`BannerNotification Component should match snapshot with error status 1`] = `
<View
accessibilityLabel="bannerTest"
accessible={true}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "row",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
[
{
"alignItems": "flex-start",
"backgroundColor": "#DB2E2E",
"columnGap": 7,
"justifyContent": "space-between",
"marginVertical": 1,
"paddingHorizontal": 18,
"paddingVertical": 12,
"position": "relative",
"width": "100%",
},
{
"backgroundColor": "#DB2E2E",
},
],
null,
null,
]
}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
{
"flex": 1,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<Text
accessibilityLabel="bannerTestText"
style={
[
{
"color": "#000000",
"fontSize": 16,
"lineHeight": 18,
},
{
"fontFamily": "Inter_600SemiBold",
"fontSize": 15,
},
{
"color": "#FFFFFF",
},
{
"textAlign": "left",
},
null,
null,
{
"fontFamily": "Inter_600SemiBold",
"fontSize": 12,
"lineHeight": 15,
"padding": 1,
"textAlignVertical": "center",
},
]
}
>
Test notification message
</Text>
</View>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<View
accessibilityLabel="close"
accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
{
"paddingLeft": 9,
}
}
/>
</View>
</View>
</View>
`;
exports[`BannerNotification Component should match snapshot with in progress status 1`] = `
<View
accessibilityLabel="bannerTest"
accessible={true}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "row",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
[
{
"alignItems": "flex-start",
"backgroundColor": "#DB2E2E",
"columnGap": 7,
"justifyContent": "space-between",
"marginVertical": 1,
"paddingHorizontal": 18,
"paddingVertical": 12,
"position": "relative",
"width": "100%",
},
{
"backgroundColor": "#D9822B",
},
],
null,
null,
]
}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
{
"flex": 1,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<Text
accessibilityLabel="bannerTestText"
style={
[
{
"color": "#000000",
"fontSize": 16,
"lineHeight": 18,
},
{
"fontFamily": "Inter_600SemiBold",
"fontSize": 15,
},
{
"color": "#FFFFFF",
},
{
"textAlign": "left",
},
null,
null,
{
"fontFamily": "Inter_600SemiBold",
"fontSize": 12,
"lineHeight": 15,
"padding": 1,
"textAlignVertical": "center",
},
]
}
>
Test notification message
</Text>
</View>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<View
accessibilityLabel="close"
accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
{
"paddingLeft": 9,
}
}
/>
</View>
</View>
</View>
`;
exports[`BannerNotification Component should match snapshot with long message 1`] = `
<View
accessibilityLabel="bannerTest"
accessible={true}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "row",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
[
{
"alignItems": "flex-start",
"backgroundColor": "#DB2E2E",
"columnGap": 7,
"justifyContent": "space-between",
"marginVertical": 1,
"paddingHorizontal": 18,
"paddingVertical": 12,
"position": "relative",
"width": "100%",
},
{
"backgroundColor": "#4B9D20",
},
],
null,
null,
]
}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
{
"flex": 1,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<Text
accessibilityLabel="bannerTestText"
style={
[
{
"color": "#000000",
"fontSize": 16,
"lineHeight": 18,
},
{
"fontFamily": "Inter_600SemiBold",
"fontSize": 15,
},
{
"color": "#FFFFFF",
},
{
"textAlign": "left",
},
null,
null,
{
"fontFamily": "Inter_600SemiBold",
"fontSize": 12,
"lineHeight": 15,
"padding": 1,
"textAlignVertical": "center",
},
]
}
>
This is a very long notification message that should wrap to multiple lines and still be displayed correctly
</Text>
</View>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<View
accessibilityLabel="close"
accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
{
"paddingLeft": 9,
}
}
/>
</View>
</View>
</View>
`;
exports[`BannerNotification Component should match snapshot with success status 1`] = `
<View
accessibilityLabel="bannerTest"
accessible={true}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "row",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
[
{
"alignItems": "flex-start",
"backgroundColor": "#DB2E2E",
"columnGap": 7,
"justifyContent": "space-between",
"marginVertical": 1,
"paddingHorizontal": 18,
"paddingVertical": 12,
"position": "relative",
"width": "100%",
},
{
"backgroundColor": "#4B9D20",
},
],
null,
null,
]
}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
{
"flex": 1,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<Text
accessibilityLabel="bannerTestText"
style={
[
{
"color": "#000000",
"fontSize": 16,
"lineHeight": 18,
},
{
"fontFamily": "Inter_600SemiBold",
"fontSize": 15,
},
{
"color": "#FFFFFF",
},
{
"textAlign": "left",
},
null,
null,
{
"fontFamily": "Inter_600SemiBold",
"fontSize": 12,
"lineHeight": 15,
"padding": 1,
"textAlignVertical": "center",
},
]
}
>
Test notification message
</Text>
</View>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<View
accessibilityLabel="close"
accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
{
"paddingLeft": 9,
}
}
/>
</View>
</View>
</View>
`;

View File

@@ -0,0 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`BannerNotificationContainer Component should match snapshot with no banners visible 1`] = `null`;
exports[`BannerNotificationContainer Component should match snapshot with verification banner disabled 1`] = `null`;
exports[`BannerNotificationContainer Component should match snapshot with verification banner enabled 1`] = `null`;

View File

@@ -0,0 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`CopilotTooltip Component should match snapshot with first step 1`] = `
[
"Step 1 Title",
"Step 1 Description",
"1/5",
]
`;

View File

@@ -0,0 +1,268 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`CopyButton Component should match snapshot with default props 1`] = `
<View
accessibilityLabel="clipboard.copyButton"
accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={false}
collapsable={false}
focusable={true}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "row",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<Text
accessibilityLabel="clipboard.copyText"
style={
[
{
"color": "#000000",
"fontSize": 16,
"lineHeight": 18,
},
{
"fontFamily": "Inter_600SemiBold",
"fontSize": 15,
},
null,
{
"textAlign": "center",
},
null,
null,
{
"maxWidth": 130,
"paddingTop": 0,
},
]
}
>
clipboard.copy
</Text>
</View>
</View>
`;
exports[`CopyButton Component should match snapshot with long content 1`] = `
<View
accessibilityLabel="clipboard.copyButton"
accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={false}
collapsable={false}
focusable={true}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "row",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<Text
accessibilityLabel="clipboard.copyText"
style={
[
{
"color": "#000000",
"fontSize": 16,
"lineHeight": 18,
},
{
"fontFamily": "Inter_600SemiBold",
"fontSize": 15,
},
null,
{
"textAlign": "center",
},
null,
null,
{
"maxWidth": 130,
"paddingTop": 0,
},
]
}
>
clipboard.copy
</Text>
</View>
</View>
`;
exports[`CopyButton Component should match snapshot with special characters 1`] = `
<View
accessibilityLabel="clipboard.copyButton"
accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={false}
collapsable={false}
focusable={true}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "row",
"justifyContent": undefined,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<Text
accessibilityLabel="clipboard.copyText"
style={
[
{
"color": "#000000",
"fontSize": 16,
"lineHeight": 18,
},
{
"fontFamily": "Inter_600SemiBold",
"fontSize": 15,
},
null,
{
"textAlign": "center",
},
null,
null,
{
"maxWidth": 130,
"paddingTop": 0,
},
]
}
>
clipboard.copy
</Text>
</View>
</View>
`;

View File

@@ -0,0 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DropdownIcon Component should match snapshot with default props 1`] = `<View />`;
exports[`DropdownIcon Component should match snapshot with different icon 1`] = `<View />`;
exports[`DropdownIcon Component should match snapshot with empty items 1`] = `<View />`;
exports[`DropdownIcon Component should match snapshot with multiple items 1`] = `<View />`;

View File

@@ -0,0 +1,170 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DualMessageOverlay Component should match snapshot with both buttons 1`] = `
[
"Confirm Action",
"Are you sure you want to proceed?",
<Text
style={
[
{
"color": "#000000",
"fontSize": 16,
"lineHeight": 18,
},
{
"fontFamily": "Inter_700Bold",
"fontSize": 15,
"justifyContent": "center",
},
{
"color": "#FFFFFF",
},
{
"textAlign": "left",
},
null,
null,
{
"paddingLeft": 0,
},
]
}
>
tryAgain
</Text>,
<Text
style={
[
{
"color": "#000000",
"fontSize": 16,
"lineHeight": 18,
},
{
"fontFamily": "Inter_700Bold",
"fontSize": 15,
"justifyContent": "center",
},
{
"color": "#FFFFFF",
},
{
"textAlign": "left",
},
null,
null,
{
"paddingLeft": 0,
},
]
}
>
ignore
</Text>,
]
`;
exports[`DualMessageOverlay Component should match snapshot with children 1`] = `
[
"Confirm Action",
"Are you sure you want to proceed?",
<Text>
Custom content here
</Text>,
]
`;
exports[`DualMessageOverlay Component should match snapshot with custom height 1`] = `
[
"Confirm Action",
"Are you sure you want to proceed?",
]
`;
exports[`DualMessageOverlay Component should match snapshot with hint text 1`] = `
[
"Confirm Action",
"Are you sure you want to proceed?",
"Additional information",
]
`;
exports[`DualMessageOverlay Component should match snapshot with only ignore button 1`] = `
[
"Confirm Action",
"Are you sure you want to proceed?",
<Text
style={
[
{
"color": "#000000",
"fontSize": 16,
"lineHeight": 18,
},
{
"fontFamily": "Inter_700Bold",
"fontSize": 15,
"justifyContent": "center",
},
{
"color": "#FFFFFF",
},
{
"textAlign": "left",
},
null,
null,
{
"paddingLeft": 0,
},
]
}
>
ignore
</Text>,
]
`;
exports[`DualMessageOverlay Component should match snapshot with only try again button 1`] = `
[
"Confirm Action",
"Are you sure you want to proceed?",
<Text
style={
[
{
"color": "#000000",
"fontSize": 16,
"lineHeight": 18,
},
{
"fontFamily": "Inter_700Bold",
"fontSize": 15,
"justifyContent": "center",
},
{
"color": "#FFFFFF",
},
{
"textAlign": "left",
},
null,
null,
{
"paddingLeft": 0,
},
]
}
>
tryAgain
</Text>,
]
`;
exports[`DualMessageOverlay Component should match snapshot with title and message 1`] = `
[
"Confirm Action",
"Are you sure you want to proceed?",
]
`;

View File

@@ -0,0 +1,55 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`EditableListItem Component should match snapshot with custom title color 1`] = `
[
"Contact Information",
"Edit your details",
"editLabel",
"editLabel",
]
`;
exports[`EditableListItem Component should match snapshot with default props 1`] = `
[
"Contact Information",
"Edit your details",
"editLabel",
"editLabel",
]
`;
exports[`EditableListItem Component should match snapshot with error state 1`] = `
[
"Contact Information",
"Edit your details",
"editLabel",
"Failed to update",
"editLabel",
]
`;
exports[`EditableListItem Component should match snapshot with progress indicator 1`] = `
[
"Contact Information",
"Edit your details",
"editLabel",
"editLabel",
]
`;
exports[`EditableListItem Component should match snapshot with single item 1`] = `
[
"Contact Information",
"Edit your details",
"editLabel",
]
`;
exports[`EditableListItem Component should match snapshot with success response 1`] = `
[
"Contact Information",
"Edit your details",
"editLabel",
"editLabel",
]
`;

View File

@@ -0,0 +1,18 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`GlobalContextProvider Component should match snapshot with children 1`] = `
<Text>
Test Child
</Text>
`;
exports[`GlobalContextProvider Component should match snapshot with multiple children 1`] = `
[
<Text>
Child 1
</Text>,
<Text>
Child 2
</Text>,
]
`;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`LanguageSelector Component should match snapshot with custom trigger component 1`] = `<View />`;
exports[`LanguageSelector Component should match snapshot with default trigger 1`] = `<View />`;

View File

@@ -0,0 +1,88 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Message Component should match snapshot with cancel button 1`] = `
<View
style={
{
"backgroundColor": "rgba(0,0,0,.6)",
"height": 1334,
"position": "absolute",
"top": 0,
"width": 750,
"zIndex": 9,
}
}
>
Test
</View>
`;
exports[`Message Component should match snapshot with hint text 1`] = `
<View
style={
{
"backgroundColor": "rgba(0,0,0,.6)",
"height": 1334,
"position": "absolute",
"top": 0,
"width": 750,
"zIndex": 9,
}
}
>
Test
Hint text
</View>
`;
exports[`Message Component should match snapshot with message only 1`] = `
<View
style={
{
"backgroundColor": "rgba(0,0,0,.6)",
"height": 1334,
"position": "absolute",
"top": 0,
"width": 750,
"zIndex": 9,
}
}
>
Test Message
</View>
`;
exports[`Message Component should match snapshot with title and message 1`] = `
<View
style={
{
"backgroundColor": "rgba(0,0,0,.6)",
"height": 1334,
"position": "absolute",
"top": 0,
"width": 750,
"zIndex": 9,
}
}
>
Title
Message
</View>
`;
exports[`Message Component should match snapshot with title only 1`] = `
<View
style={
{
"backgroundColor": "rgba(0,0,0,.6)",
"height": 1334,
"position": "absolute",
"top": 0,
"width": 750,
"zIndex": 9,
}
}
>
Test Title
</View>
`;

View File

@@ -0,0 +1,68 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ErrorMessageOverlay Component should match snapshot with error 1`] = `
[
"network.title",
"network.message",
]
`;
exports[`ErrorMessageOverlay Component should match snapshot with testID 1`] = `
[
"network.title",
"network.message",
]
`;
exports[`MessageOverlay Component should match snapshot with button 1`] = `
[
"Test Title",
"Test Message",
]
`;
exports[`MessageOverlay Component should match snapshot with custom children 1`] = `
[
"Test Title",
"Test Message",
<Text>
Custom Content
</Text>,
]
`;
exports[`MessageOverlay Component should match snapshot with custom minHeight 1`] = `
[
"Test Title",
"Test Message",
]
`;
exports[`MessageOverlay Component should match snapshot with hint text 1`] = `
[
"Test Title",
"Test Message",
"This is a hint",
]
`;
exports[`MessageOverlay Component should match snapshot with numeric progress 1`] = `
[
"Test Title",
"Test Message",
]
`;
exports[`MessageOverlay Component should match snapshot with progress indicator 1`] = `
[
"Test Title",
"Test Message",
]
`;
exports[`MessageOverlay Component should match snapshot with title and message 1`] = `
[
"Test Title",
"Test Message",
]
`;

View File

@@ -0,0 +1,629 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Passcode Component should match snapshot with both message and error 1`] = `
<Modal
animationType="slide"
hardwareAccelerated={false}
onRequestClose={[MockFunction]}
style={
{
"height": 1334,
"width": 750,
}
}
visible={true}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
{
"flex": 1,
},
{
"paddingBottom": 32,
"paddingEnd": 32,
"paddingStart": 32,
"paddingTop": 32,
},
null,
{
"backgroundColor": "#FFFFFF",
},
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
{
"flex": 1,
},
null,
null,
null,
{
"width": "100%",
},
null,
{
"justifyContent": "space-between",
},
null,
null,
null,
null,
null,
]
}
>
<Text
style={
[
{
"color": "#000000",
"fontSize": 16,
"lineHeight": 18,
},
{
"fontFamily": "Inter_400Regular",
"fontSize": 14,
},
null,
{
"textAlign": "center",
},
null,
null,
null,
]
}
>
Enter passcode
</Text>
</View>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
{
"flex": 1,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<Text
style={
[
{
"color": "#000000",
"fontSize": 16,
"lineHeight": 18,
},
{
"fontFamily": "Inter_400Regular",
"fontSize": 14,
},
{
"color": "#D52929",
},
{
"textAlign": "center",
},
null,
null,
null,
]
}
>
Authentication failed
</Text>
</View>
</View>
</Modal>
`;
exports[`Passcode Component should match snapshot with custom message 1`] = `
<Modal
animationType="slide"
hardwareAccelerated={false}
onRequestClose={[MockFunction]}
style={
{
"height": 1334,
"width": 750,
}
}
visible={true}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
{
"flex": 1,
},
{
"paddingBottom": 32,
"paddingEnd": 32,
"paddingStart": 32,
"paddingTop": 32,
},
null,
{
"backgroundColor": "#FFFFFF",
},
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
{
"flex": 1,
},
null,
null,
null,
{
"width": "100%",
},
null,
{
"justifyContent": "space-between",
},
null,
null,
null,
null,
null,
]
}
>
<Text
style={
[
{
"color": "#000000",
"fontSize": 16,
"lineHeight": 18,
},
{
"fontFamily": "Inter_400Regular",
"fontSize": 14,
},
null,
{
"textAlign": "center",
},
null,
null,
null,
]
}
>
Please enter your 6-digit passcode
</Text>
</View>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
{
"flex": 1,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<Text
style={
[
{
"color": "#000000",
"fontSize": 16,
"lineHeight": 18,
},
{
"fontFamily": "Inter_400Regular",
"fontSize": 14,
},
{
"color": "#D52929",
},
{
"textAlign": "center",
},
null,
null,
null,
]
}
/>
</View>
</View>
</Modal>
`;
exports[`Passcode Component should match snapshot with default props 1`] = `
<Modal
animationType="slide"
hardwareAccelerated={false}
onRequestClose={[MockFunction]}
style={
{
"height": 1334,
"width": 750,
}
}
visible={true}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
{
"flex": 1,
},
{
"paddingBottom": 32,
"paddingEnd": 32,
"paddingStart": 32,
"paddingTop": 32,
},
null,
{
"backgroundColor": "#FFFFFF",
},
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
{
"flex": 1,
},
null,
null,
null,
{
"width": "100%",
},
null,
{
"justifyContent": "space-between",
},
null,
null,
null,
null,
null,
]
}
>
<Text
style={
[
{
"color": "#000000",
"fontSize": 16,
"lineHeight": 18,
},
{
"fontFamily": "Inter_400Regular",
"fontSize": 14,
},
null,
{
"textAlign": "center",
},
null,
null,
null,
]
}
>
Enter your passcode
</Text>
</View>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
{
"flex": 1,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<Text
style={
[
{
"color": "#000000",
"fontSize": 16,
"lineHeight": 18,
},
{
"fontFamily": "Inter_400Regular",
"fontSize": 14,
},
{
"color": "#D52929",
},
{
"textAlign": "center",
},
null,
null,
null,
]
}
/>
</View>
</View>
</Modal>
`;
exports[`Passcode Component should match snapshot with error message 1`] = `
<Modal
animationType="slide"
hardwareAccelerated={false}
onRequestClose={[MockFunction]}
style={
{
"height": 1334,
"width": 750,
}
}
visible={true}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
{
"flex": 1,
},
{
"paddingBottom": 32,
"paddingEnd": 32,
"paddingStart": 32,
"paddingTop": 32,
},
null,
{
"backgroundColor": "#FFFFFF",
},
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
{
"flex": 1,
},
null,
null,
null,
{
"width": "100%",
},
null,
{
"justifyContent": "space-between",
},
null,
null,
null,
null,
null,
]
}
>
<Text
style={
[
{
"color": "#000000",
"fontSize": 16,
"lineHeight": 18,
},
{
"fontFamily": "Inter_400Regular",
"fontSize": 14,
},
null,
{
"textAlign": "center",
},
null,
null,
null,
]
}
>
Enter your passcode
</Text>
</View>
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "column",
"justifyContent": undefined,
},
{
"flex": 1,
},
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
]
}
>
<Text
style={
[
{
"color": "#000000",
"fontSize": 16,
"lineHeight": 18,
},
{
"fontFamily": "Inter_400Regular",
"fontSize": 14,
},
{
"color": "#D52929",
},
{
"textAlign": "center",
},
null,
null,
null,
]
}
>
Incorrect passcode. Try again.
</Text>
</View>
</View>
</Modal>
`;

View File

@@ -0,0 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PasscodeVerify Component should match snapshot with default props 1`] = `null`;
exports[`PasscodeVerify Component should match snapshot with different testID 1`] = `null`;
exports[`PasscodeVerify Component should match snapshot without onError handler 1`] = `null`;

View File

@@ -0,0 +1,39 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PendingIcon should match snapshot 1`] = `
<View
style={
{
"marginRight": 3,
}
}
>
<View
style={
{
"backgroundColor": "white",
"borderRadius": 10,
}
}
/>
</View>
`;
exports[`PendingIcon should render with custom color 1`] = `
<View
style={
{
"marginRight": 3,
}
}
>
<View
style={
{
"backgroundColor": "white",
"borderRadius": 10,
}
}
/>
</View>
`;

View File

@@ -0,0 +1,719 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PinInput Component should match snapshot with 4 digit PIN 1`] = `
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "row",
"justifyContent": undefined,
},
null,
null,
null,
null,
{
"width": "66.66666666666667%",
},
null,
null,
null,
null,
null,
null,
null,
]
}
>
<TextInput
contextMenuHidden={true}
keyboardType="numeric"
maxLength={1}
onChangeText={[Function]}
onFocus={[Function]}
onKeyPress={[Function]}
secureTextEntry={true}
selectTextOnFocus={true}
selectionColor="#951F6F"
style={
{
"borderBottomWidth": 3,
"borderColor": "#951F6F",
"color": "#000000",
"flex": 1,
"fontFamily": "Inter_700Bold",
"fontSize": 29,
"height": 50,
"margin": 8,
"textAlign": "center",
}
}
value=""
/>
<TextInput
contextMenuHidden={true}
keyboardType="numeric"
maxLength={1}
onChangeText={[Function]}
onFocus={[Function]}
onKeyPress={[Function]}
secureTextEntry={true}
selectTextOnFocus={true}
selectionColor="#951F6F"
style={
{
"borderBottomWidth": 3,
"borderColor": "#C7C7C7",
"color": "#000000",
"flex": 1,
"fontFamily": "Inter_600SemiBold",
"fontSize": 33,
"height": 50,
"lineHeight": 28,
"margin": 8,
"textAlign": "center",
}
}
value=""
/>
<TextInput
contextMenuHidden={true}
keyboardType="numeric"
maxLength={1}
onChangeText={[Function]}
onFocus={[Function]}
onKeyPress={[Function]}
secureTextEntry={true}
selectTextOnFocus={true}
selectionColor="#951F6F"
style={
{
"borderBottomWidth": 3,
"borderColor": "#C7C7C7",
"color": "#000000",
"flex": 1,
"fontFamily": "Inter_600SemiBold",
"fontSize": 33,
"height": 50,
"lineHeight": 28,
"margin": 8,
"textAlign": "center",
}
}
value=""
/>
<TextInput
contextMenuHidden={true}
keyboardType="numeric"
maxLength={1}
onChangeText={[Function]}
onFocus={[Function]}
onKeyPress={[Function]}
secureTextEntry={true}
selectTextOnFocus={true}
selectionColor="#951F6F"
style={
{
"borderBottomWidth": 3,
"borderColor": "#C7C7C7",
"color": "#000000",
"flex": 1,
"fontFamily": "Inter_600SemiBold",
"fontSize": 33,
"height": 50,
"lineHeight": 28,
"margin": 8,
"textAlign": "center",
}
}
value=""
/>
</View>
`;
exports[`PinInput Component should match snapshot with 6 digit PIN 1`] = `
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "row",
"justifyContent": undefined,
},
null,
null,
null,
null,
{
"width": "100%",
},
null,
null,
null,
null,
null,
null,
null,
]
}
>
<TextInput
contextMenuHidden={true}
keyboardType="numeric"
maxLength={1}
onChangeText={[Function]}
onFocus={[Function]}
onKeyPress={[Function]}
secureTextEntry={true}
selectTextOnFocus={true}
selectionColor="#951F6F"
style={
{
"borderBottomWidth": 3,
"borderColor": "#951F6F",
"color": "#000000",
"flex": 1,
"fontFamily": "Inter_700Bold",
"fontSize": 29,
"height": 50,
"margin": 8,
"textAlign": "center",
}
}
value=""
/>
<TextInput
contextMenuHidden={true}
keyboardType="numeric"
maxLength={1}
onChangeText={[Function]}
onFocus={[Function]}
onKeyPress={[Function]}
secureTextEntry={true}
selectTextOnFocus={true}
selectionColor="#951F6F"
style={
{
"borderBottomWidth": 3,
"borderColor": "#C7C7C7",
"color": "#000000",
"flex": 1,
"fontFamily": "Inter_600SemiBold",
"fontSize": 33,
"height": 50,
"lineHeight": 28,
"margin": 8,
"textAlign": "center",
}
}
value=""
/>
<TextInput
contextMenuHidden={true}
keyboardType="numeric"
maxLength={1}
onChangeText={[Function]}
onFocus={[Function]}
onKeyPress={[Function]}
secureTextEntry={true}
selectTextOnFocus={true}
selectionColor="#951F6F"
style={
{
"borderBottomWidth": 3,
"borderColor": "#C7C7C7",
"color": "#000000",
"flex": 1,
"fontFamily": "Inter_600SemiBold",
"fontSize": 33,
"height": 50,
"lineHeight": 28,
"margin": 8,
"textAlign": "center",
}
}
value=""
/>
<TextInput
contextMenuHidden={true}
keyboardType="numeric"
maxLength={1}
onChangeText={[Function]}
onFocus={[Function]}
onKeyPress={[Function]}
secureTextEntry={true}
selectTextOnFocus={true}
selectionColor="#951F6F"
style={
{
"borderBottomWidth": 3,
"borderColor": "#C7C7C7",
"color": "#000000",
"flex": 1,
"fontFamily": "Inter_600SemiBold",
"fontSize": 33,
"height": 50,
"lineHeight": 28,
"margin": 8,
"textAlign": "center",
}
}
value=""
/>
<TextInput
contextMenuHidden={true}
keyboardType="numeric"
maxLength={1}
onChangeText={[Function]}
onFocus={[Function]}
onKeyPress={[Function]}
secureTextEntry={true}
selectTextOnFocus={true}
selectionColor="#951F6F"
style={
{
"borderBottomWidth": 3,
"borderColor": "#C7C7C7",
"color": "#000000",
"flex": 1,
"fontFamily": "Inter_600SemiBold",
"fontSize": 33,
"height": 50,
"lineHeight": 28,
"margin": 8,
"textAlign": "center",
}
}
value=""
/>
<TextInput
contextMenuHidden={true}
keyboardType="numeric"
maxLength={1}
onChangeText={[Function]}
onFocus={[Function]}
onKeyPress={[Function]}
secureTextEntry={true}
selectTextOnFocus={true}
selectionColor="#951F6F"
style={
{
"borderBottomWidth": 3,
"borderColor": "#C7C7C7",
"color": "#000000",
"flex": 1,
"fontFamily": "Inter_600SemiBold",
"fontSize": 33,
"height": 50,
"lineHeight": 28,
"margin": 8,
"textAlign": "center",
}
}
value=""
/>
</View>
`;
exports[`PinInput Component should match snapshot with custom testID 1`] = `
<View
accessibilityLabel="customPinInput"
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "row",
"justifyContent": undefined,
},
null,
null,
null,
null,
{
"width": "66.66666666666667%",
},
null,
null,
null,
null,
null,
null,
null,
]
}
>
<TextInput
contextMenuHidden={true}
keyboardType="numeric"
maxLength={1}
onChangeText={[Function]}
onFocus={[Function]}
onKeyPress={[Function]}
secureTextEntry={true}
selectTextOnFocus={true}
selectionColor="#951F6F"
style={
{
"borderBottomWidth": 3,
"borderColor": "#951F6F",
"color": "#000000",
"flex": 1,
"fontFamily": "Inter_700Bold",
"fontSize": 29,
"height": 50,
"margin": 8,
"textAlign": "center",
}
}
value=""
/>
<TextInput
contextMenuHidden={true}
keyboardType="numeric"
maxLength={1}
onChangeText={[Function]}
onFocus={[Function]}
onKeyPress={[Function]}
secureTextEntry={true}
selectTextOnFocus={true}
selectionColor="#951F6F"
style={
{
"borderBottomWidth": 3,
"borderColor": "#C7C7C7",
"color": "#000000",
"flex": 1,
"fontFamily": "Inter_600SemiBold",
"fontSize": 33,
"height": 50,
"lineHeight": 28,
"margin": 8,
"textAlign": "center",
}
}
value=""
/>
<TextInput
contextMenuHidden={true}
keyboardType="numeric"
maxLength={1}
onChangeText={[Function]}
onFocus={[Function]}
onKeyPress={[Function]}
secureTextEntry={true}
selectTextOnFocus={true}
selectionColor="#951F6F"
style={
{
"borderBottomWidth": 3,
"borderColor": "#C7C7C7",
"color": "#000000",
"flex": 1,
"fontFamily": "Inter_600SemiBold",
"fontSize": 33,
"height": 50,
"lineHeight": 28,
"margin": 8,
"textAlign": "center",
}
}
value=""
/>
<TextInput
contextMenuHidden={true}
keyboardType="numeric"
maxLength={1}
onChangeText={[Function]}
onFocus={[Function]}
onKeyPress={[Function]}
secureTextEntry={true}
selectTextOnFocus={true}
selectionColor="#951F6F"
style={
{
"borderBottomWidth": 3,
"borderColor": "#C7C7C7",
"color": "#000000",
"flex": 1,
"fontFamily": "Inter_600SemiBold",
"fontSize": 33,
"height": 50,
"lineHeight": 28,
"margin": 8,
"textAlign": "center",
}
}
value=""
/>
</View>
`;
exports[`PinInput Component should match snapshot with onChange handler 1`] = `
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "row",
"justifyContent": undefined,
},
null,
null,
null,
null,
{
"width": "66.66666666666667%",
},
null,
null,
null,
null,
null,
null,
null,
]
}
>
<TextInput
contextMenuHidden={true}
keyboardType="numeric"
maxLength={1}
onChangeText={[Function]}
onFocus={[Function]}
onKeyPress={[Function]}
secureTextEntry={true}
selectTextOnFocus={true}
selectionColor="#951F6F"
style={
{
"borderBottomWidth": 3,
"borderColor": "#951F6F",
"color": "#000000",
"flex": 1,
"fontFamily": "Inter_700Bold",
"fontSize": 29,
"height": 50,
"margin": 8,
"textAlign": "center",
}
}
value=""
/>
<TextInput
contextMenuHidden={true}
keyboardType="numeric"
maxLength={1}
onChangeText={[Function]}
onFocus={[Function]}
onKeyPress={[Function]}
secureTextEntry={true}
selectTextOnFocus={true}
selectionColor="#951F6F"
style={
{
"borderBottomWidth": 3,
"borderColor": "#C7C7C7",
"color": "#000000",
"flex": 1,
"fontFamily": "Inter_600SemiBold",
"fontSize": 33,
"height": 50,
"lineHeight": 28,
"margin": 8,
"textAlign": "center",
}
}
value=""
/>
<TextInput
contextMenuHidden={true}
keyboardType="numeric"
maxLength={1}
onChangeText={[Function]}
onFocus={[Function]}
onKeyPress={[Function]}
secureTextEntry={true}
selectTextOnFocus={true}
selectionColor="#951F6F"
style={
{
"borderBottomWidth": 3,
"borderColor": "#C7C7C7",
"color": "#000000",
"flex": 1,
"fontFamily": "Inter_600SemiBold",
"fontSize": 33,
"height": 50,
"lineHeight": 28,
"margin": 8,
"textAlign": "center",
}
}
value=""
/>
<TextInput
contextMenuHidden={true}
keyboardType="numeric"
maxLength={1}
onChangeText={[Function]}
onFocus={[Function]}
onKeyPress={[Function]}
secureTextEntry={true}
selectTextOnFocus={true}
selectionColor="#951F6F"
style={
{
"borderBottomWidth": 3,
"borderColor": "#C7C7C7",
"color": "#000000",
"flex": 1,
"fontFamily": "Inter_600SemiBold",
"fontSize": 33,
"height": 50,
"lineHeight": 28,
"margin": 8,
"textAlign": "center",
}
}
value=""
/>
</View>
`;
exports[`PinInput Component should match snapshot with onDone and autosubmit 1`] = `
<View
accessible={true}
style={
[
{
"alignItems": undefined,
"flexDirection": "row",
"justifyContent": undefined,
},
null,
null,
null,
null,
{
"width": "66.66666666666667%",
},
null,
null,
null,
null,
null,
null,
null,
]
}
>
<TextInput
contextMenuHidden={true}
keyboardType="numeric"
maxLength={1}
onChangeText={[Function]}
onFocus={[Function]}
onKeyPress={[Function]}
secureTextEntry={true}
selectTextOnFocus={true}
selectionColor="#951F6F"
style={
{
"borderBottomWidth": 3,
"borderColor": "#951F6F",
"color": "#000000",
"flex": 1,
"fontFamily": "Inter_700Bold",
"fontSize": 29,
"height": 50,
"margin": 8,
"textAlign": "center",
}
}
value=""
/>
<TextInput
contextMenuHidden={true}
keyboardType="numeric"
maxLength={1}
onChangeText={[Function]}
onFocus={[Function]}
onKeyPress={[Function]}
secureTextEntry={true}
selectTextOnFocus={true}
selectionColor="#951F6F"
style={
{
"borderBottomWidth": 3,
"borderColor": "#C7C7C7",
"color": "#000000",
"flex": 1,
"fontFamily": "Inter_600SemiBold",
"fontSize": 33,
"height": 50,
"lineHeight": 28,
"margin": 8,
"textAlign": "center",
}
}
value=""
/>
<TextInput
contextMenuHidden={true}
keyboardType="numeric"
maxLength={1}
onChangeText={[Function]}
onFocus={[Function]}
onKeyPress={[Function]}
secureTextEntry={true}
selectTextOnFocus={true}
selectionColor="#951F6F"
style={
{
"borderBottomWidth": 3,
"borderColor": "#C7C7C7",
"color": "#000000",
"flex": 1,
"fontFamily": "Inter_600SemiBold",
"fontSize": 33,
"height": 50,
"lineHeight": 28,
"margin": 8,
"textAlign": "center",
}
}
value=""
/>
<TextInput
contextMenuHidden={true}
keyboardType="numeric"
maxLength={1}
onChangeText={[Function]}
onFocus={[Function]}
onKeyPress={[Function]}
secureTextEntry={true}
selectTextOnFocus={true}
selectionColor="#951F6F"
style={
{
"borderBottomWidth": 3,
"borderColor": "#C7C7C7",
"color": "#000000",
"flex": 1,
"fontFamily": "Inter_600SemiBold",
"fontSize": 33,
"height": 50,
"lineHeight": 28,
"margin": 8,
"textAlign": "center",
}
}
value=""
/>
</View>
`;

View File

@@ -0,0 +1,82 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ProfileIcon Component should match snapshot with custom container styles 1`] = `
<View
accessibilityLabel="ProfileIconOuter"
accessible={true}
style={
{
"position": "relative",
}
}
>
<View
accessibilityLabel="ProfileIconInner"
accessible={true}
style={
{
"backgroundColor": "blue",
"borderRadius": 10,
"padding": 5,
}
}
/>
</View>
`;
exports[`ProfileIcon Component should match snapshot with custom icon size 1`] = `
<View
accessibilityLabel="ProfileIconOuter"
accessible={true}
style={
{
"position": "relative",
}
}
>
<View
accessibilityLabel="ProfileIconInner"
accessible={true}
style={{}}
/>
</View>
`;
exports[`ProfileIcon Component should match snapshot with pinned icon 1`] = `
<View
accessibilityLabel="ProfileIconOuter"
accessible={true}
style={
{
"position": "relative",
}
}
>
<View
accessibilityLabel="ProfileIconInner"
accessible={true}
style={{}}
/>
<View
testID="mockPinIcon"
/>
</View>
`;
exports[`ProfileIcon Component should match snapshot without pinned icon 1`] = `
<View
accessibilityLabel="ProfileIconOuter"
accessible={true}
style={
{
"position": "relative",
}
}
>
<View
accessibilityLabel="ProfileIconInner"
accessible={true}
style={{}}
/>
</View>
`;

View File

@@ -0,0 +1,25 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ProgressingModal Component should match snapshot as requester 1`] = `null`;
exports[`ProgressingModal Component should match snapshot with BLE error visible 1`] = `"Bluetooth error occurred"`;
exports[`ProgressingModal Component should match snapshot with default props 1`] = `null`;
exports[`ProgressingModal Component should match snapshot with hint visible 1`] = `"Please wait..."`;
exports[`ProgressingModal Component should match snapshot with progress spinner 1`] = `
<Spinner
color="#951F6F"
style={
{
"marginLeft": 6,
}
}
type="ThreeBounce"
/>
`;
exports[`ProgressingModal Component should match snapshot with retry button 1`] = `"Connection failed"`;
exports[`ProgressingModal Component should match snapshot with stay in progress button 1`] = `"Taking longer than expected"`;

View File

@@ -0,0 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`QrCodeOverlay Component should match snapshot with default props 1`] = `null`;
exports[`QrCodeOverlay Component should match snapshot with force visible 1`] = `null`;
exports[`QrCodeOverlay Component should match snapshot with inline QR disabled 1`] = `null`;
exports[`QrCodeOverlay Component should match snapshot with onClose handler 1`] = `null`;

View File

@@ -0,0 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`QrScanner Component should match snapshot with custom title 1`] = `<View />`;
exports[`QrScanner Component should match snapshot with default props 1`] = `<View />`;
exports[`QrScanner Component should match snapshot with title 1`] = `<View />`;

View File

@@ -0,0 +1,165 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SectionLayout Component should match snapshot with complex children 1`] = `
<View
accessibilityLabel="testSection"
accessible={true}
style={
{
"marginBottom": 0,
"marginLeft": 18,
"marginRight": 18,
"marginTop": 16,
"rowGap": 2,
}
}
>
<View
testID="headerIcon"
>
Icon
</View>
Section Header
<Text>
Line 1
</Text>
<Text>
Line 2
</Text>
<View>
<Text>
Nested content
</Text>
</View>
</View>
`;
exports[`SectionLayout Component should match snapshot with custom marginBottom 1`] = `
<View
accessibilityLabel="testSection"
accessible={true}
style={
{
"marginBottom": 20,
"marginLeft": 18,
"marginRight": 18,
"marginTop": 16,
"rowGap": 2,
}
}
>
<View
testID="headerIcon"
>
Icon
</View>
Section Header
<Text>
Section Content
</Text>
</View>
`;
exports[`SectionLayout Component should match snapshot with default props 1`] = `
<View
accessibilityLabel="testSection"
accessible={true}
style={
{
"marginBottom": 0,
"marginLeft": 18,
"marginRight": 18,
"marginTop": 16,
"rowGap": 2,
}
}
>
<View
testID="headerIcon"
>
Icon
</View>
Section Header
<Text>
Section Content
</Text>
</View>
`;
exports[`SectionLayout Component should match snapshot with different header text 1`] = `
<View
accessibilityLabel="testSection"
accessible={true}
style={
{
"marginBottom": 0,
"marginLeft": 18,
"marginRight": 18,
"marginTop": 16,
"rowGap": 2,
}
}
>
<View
testID="headerIcon"
>
Icon
</View>
Custom Section Title
<Text>
Section Content
</Text>
</View>
`;
exports[`SectionLayout Component should match snapshot with different icon 1`] = `
<View
accessibilityLabel="testSection"
accessible={true}
style={
{
"marginBottom": 0,
"marginLeft": 18,
"marginRight": 18,
"marginTop": 16,
"rowGap": 2,
}
}
>
<View
testID="customIcon"
>
🔍
</View>
Section Header
<Text>
Section Content
</Text>
</View>
`;
exports[`SectionLayout Component should match snapshot with zero marginBottom 1`] = `
<View
accessibilityLabel="testSection"
accessible={true}
style={
{
"marginBottom": 0,
"marginLeft": 18,
"marginRight": 18,
"marginTop": 16,
"rowGap": 2,
}
}
>
<View
testID="headerIcon"
>
Icon
</View>
Section Header
<Text>
Section Content
</Text>
</View>
`;

View File

@@ -0,0 +1,501 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`TextEditOverlay Component should match snapshot with default props 1`] = `
<View
style={
{
"backgroundColor": "rgba(0,0,0,.6)",
"height": 1334,
"position": "absolute",
"top": 0,
"width": 750,
"zIndex": 9,
}
}
>
Edit Name
<TextInput
onChangeText={[MockFunction]}
testID="text-input"
value="John Doe"
/>
<View
accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
{
"opacity": 1,
}
}
testID="button-cancel"
>
<Text>
cancel
</Text>
</View>
<View
accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
{
"opacity": 1,
}
}
testID="button-save"
>
<Text>
save
</Text>
</View>
</View>
`;
exports[`TextEditOverlay Component should match snapshot with different label 1`] = `
<View
style={
{
"backgroundColor": "rgba(0,0,0,.6)",
"height": 1334,
"position": "absolute",
"top": 0,
"width": 750,
"zIndex": 9,
}
}
>
Edit Email
<TextInput
onChangeText={[MockFunction]}
testID="text-input"
value="John Doe"
/>
<View
accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
{
"opacity": 1,
}
}
testID="button-cancel"
>
<Text>
cancel
</Text>
</View>
<View
accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
{
"opacity": 1,
}
}
testID="button-save"
>
<Text>
save
</Text>
</View>
</View>
`;
exports[`TextEditOverlay Component should match snapshot with empty value 1`] = `
<View
style={
{
"backgroundColor": "rgba(0,0,0,.6)",
"height": 1334,
"position": "absolute",
"top": 0,
"width": 750,
"zIndex": 9,
}
}
>
Edit Name
<TextInput
onChangeText={[MockFunction]}
testID="text-input"
value=""
/>
<View
accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
{
"opacity": 1,
}
}
testID="button-cancel"
>
<Text>
cancel
</Text>
</View>
<View
accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
{
"opacity": 1,
}
}
testID="button-save"
>
<Text>
save
</Text>
</View>
</View>
`;
exports[`TextEditOverlay Component should match snapshot with long value 1`] = `
<View
style={
{
"backgroundColor": "rgba(0,0,0,.6)",
"height": 1334,
"position": "absolute",
"top": 0,
"width": 750,
"zIndex": 9,
}
}
>
Edit Name
<TextInput
onChangeText={[MockFunction]}
testID="text-input"
value="This is a very long text value that should be editable"
/>
<View
accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
{
"opacity": 1,
}
}
testID="button-cancel"
>
<Text>
cancel
</Text>
</View>
<View
accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
{
"opacity": 1,
}
}
testID="button-save"
>
<Text>
save
</Text>
</View>
</View>
`;
exports[`TextEditOverlay Component should match snapshot with maxLength 1`] = `
<View
style={
{
"backgroundColor": "rgba(0,0,0,.6)",
"height": 1334,
"position": "absolute",
"top": 0,
"width": 750,
"zIndex": 9,
}
}
>
Edit Name
<TextInput
onChangeText={[MockFunction]}
testID="text-input"
value="John Doe"
/>
<View
accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
{
"opacity": 1,
}
}
testID="button-cancel"
>
<Text>
cancel
</Text>
</View>
<View
accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
{
"opacity": 1,
}
}
testID="button-save"
>
<Text>
save
</Text>
</View>
</View>
`;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,104 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`VcItemContainerProfileImage Component should match snapshot with empty string face 1`] = `
<View
testID="mockProfileIcon"
/>
`;
exports[`VcItemContainerProfileImage Component should match snapshot with face image 1`] = `
<View
accessibilityIgnoresInvertColors={true}
style={
{
"borderRadius": 100,
"height": 53,
"width": 40,
}
}
>
<Image
source={
{
"uri": "https://example.com/avatar.jpg",
}
}
style={
[
{
"bottom": 0,
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
},
{
"height": 53,
"width": 40,
},
{
"borderRadius": 10,
"height": 53,
"width": 40,
},
]
}
/>
</View>
`;
exports[`VcItemContainerProfileImage Component should match snapshot with face image and pinned 1`] = `
<View
accessibilityIgnoresInvertColors={true}
style={
{
"borderRadius": 100,
"height": 53,
"width": 40,
}
}
>
<Image
source={
{
"uri": "https://example.com/avatar.jpg",
}
}
style={
[
{
"bottom": 0,
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
},
{
"height": 53,
"width": 40,
},
{
"borderRadius": 10,
"height": 53,
"width": 40,
},
]
}
/>
<View
testID="mockPinIcon"
/>
</View>
`;
exports[`VcItemContainerProfileImage Component should match snapshot without face image 1`] = `
<View
testID="mockProfileIcon"
/>
`;
exports[`VcItemContainerProfileImage Component should match snapshot without face image and pinned 1`] = `
<View
testID="mockProfileIcon"
/>
`;

View File

@@ -0,0 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`VerifiedIcon should match snapshot 1`] = `
<View
style={
{
"marginRight": 3,
}
}
>
<View
style={
{
"backgroundColor": "white",
"borderRadius": 10,
}
}
/>
</View>
`;

View File

@@ -0,0 +1,120 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {Column, Row} from './Layout';
import {Text} from 'react-native';
describe('Layout Components', () => {
describe('Column Component', () => {
it('should render Column component', () => {
const {toJSON} = render(
<Column>
<Text>Test</Text>
</Column>,
);
expect(toJSON()).toBeTruthy();
});
it('should render children in column', () => {
const {getByText} = render(
<Column>
<Text>Child 1</Text>
<Text>Child 2</Text>
</Column>,
);
expect(getByText('Child 1')).toBeTruthy();
expect(getByText('Child 2')).toBeTruthy();
});
it('should render with fill prop', () => {
const {getByLabelText} = render(
<Column fill testID="fill-column">
<Text>Fill Column</Text>
</Column>,
);
expect(getByLabelText('fill-column')).toBeTruthy();
});
it('should render with multiple layout props', () => {
const {getByLabelText} = render(
<Column
testID="complex-column"
fill
padding="10"
margin="10 20"
backgroundColor="#FF0000"
align="center"
crossAlign="center">
<Text>Complex Column</Text>
</Column>,
);
expect(getByLabelText('complex-column')).toBeTruthy();
});
});
describe('Row Component', () => {
it('should render Row component', () => {
const {toJSON} = render(
<Row>
<Text>Test</Text>
</Row>,
);
expect(toJSON()).toBeTruthy();
});
it('should render children in row', () => {
const {getByText} = render(
<Row>
<Text>Item 1</Text>
<Text>Item 2</Text>
<Text>Item 3</Text>
</Row>,
);
expect(getByText('Item 1')).toBeTruthy();
expect(getByText('Item 2')).toBeTruthy();
expect(getByText('Item 3')).toBeTruthy();
});
it('should render with fill prop', () => {
const {getByLabelText} = render(
<Row fill testID="fill-row">
<Text>Fill Row</Text>
</Row>,
);
expect(getByLabelText('fill-row')).toBeTruthy();
});
it('should render with multiple layout props', () => {
const {getByLabelText} = render(
<Row
testID="complex-row"
fill
padding="10"
margin="5"
backgroundColor="#0000FF"
align="center"
crossAlign="center">
<Text>Complex Row</Text>
</Row>,
);
expect(getByLabelText('complex-row')).toBeTruthy();
});
it('should handle nested layouts', () => {
const {getByText} = render(
<Row>
<Column>
<Text>Nested 1</Text>
</Column>
<Column>
<Text>Nested 2</Text>
</Column>
</Row>,
);
expect(getByText('Nested 1')).toBeTruthy();
expect(getByText('Nested 2')).toBeTruthy();
});
});
});

View File

@@ -0,0 +1,111 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {TextItem} from './TextItem';
describe('TextItem', () => {
it('should render text without label', () => {
const {getByText} = render(<TextItem text="Test Text" />);
expect(getByText('Test Text')).toBeTruthy();
});
it('should render text with label', () => {
const {getByText} = render(
<TextItem text="Main Text" label="Label Text" />,
);
expect(getByText('Main Text')).toBeTruthy();
expect(getByText('Label Text')).toBeTruthy();
});
it('should render only text when label is not provided', () => {
const {getByText, queryByText} = render(<TextItem text="Only Text" />);
expect(getByText('Only Text')).toBeTruthy();
expect(queryByText('Label Text')).toBeNull();
});
it('should render with testID prop', () => {
const {toJSON} = render(<TextItem text="Test" testID="customTestID" />);
expect(toJSON()).toBeTruthy();
});
it('should render with divider prop', () => {
const {getByText} = render(<TextItem text="Test" divider={true} />);
expect(getByText('Test')).toBeTruthy();
});
it('should render without divider when not specified', () => {
const {getByText} = render(<TextItem text="Test" divider={false} />);
expect(getByText('Test')).toBeTruthy();
});
it('should render with topDivider prop', () => {
const {getByText} = render(<TextItem text="Test" topDivider={true} />);
expect(getByText('Test')).toBeTruthy();
});
it('should render without topDivider when not specified', () => {
const {getByText} = render(<TextItem text="Test" topDivider={false} />);
expect(getByText('Test')).toBeTruthy();
});
it('should render with both dividers', () => {
const {getByText} = render(
<TextItem text="Test" divider={true} topDivider={true} />,
);
expect(getByText('Test')).toBeTruthy();
});
it('should render with custom margin when provided', () => {
const {getByText} = render(<TextItem text="Test" margin="10" />);
expect(getByText('Test')).toBeTruthy();
});
it('should render long text correctly', () => {
const longText = 'A'.repeat(200);
const {getByText} = render(<TextItem text={longText} />);
expect(getByText(longText)).toBeTruthy();
});
it('should handle empty text', () => {
const {toJSON} = render(<TextItem text="" />);
expect(toJSON()).toBeTruthy();
});
it('should handle special characters in text', () => {
const specialText = '!@#$%^&*()_+-={}[]|:";\'<>?,./';
const {getByText} = render(<TextItem text={specialText} />);
expect(getByText(specialText)).toBeTruthy();
});
it('should render with all props combined', () => {
const {getByText} = render(
<TextItem
text="Complete Test"
label="All Props"
testID="allProps"
divider={true}
topDivider={true}
margin="20"
/>,
);
expect(getByText('Complete Test')).toBeTruthy();
expect(getByText('All Props')).toBeTruthy();
});
it('should render multiple TextItems', () => {
const {getByText} = render(
<>
<TextItem text="First" label="One" />
<TextItem text="Second" label="Two" />
<TextItem text="Third" label="Three" />
</>,
);
expect(getByText('First')).toBeTruthy();
expect(getByText('Second')).toBeTruthy();
expect(getByText('Third')).toBeTruthy();
expect(getByText('One')).toBeTruthy();
expect(getByText('Two')).toBeTruthy();
expect(getByText('Three')).toBeTruthy();
});
});

View File

@@ -0,0 +1,87 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {Timestamp} from './Timestamp';
describe('Timestamp', () => {
it('should render formatted date and time', () => {
// March 15, 2024 at 14:30:00
const timestamp = new Date(2024, 2, 15, 14, 30, 0).getTime();
const {getByText} = render(<Timestamp time={timestamp} testId="test" />);
const formatted = getByText(/15 March 2024/);
expect(formatted).toBeTruthy();
expect(formatted.props.children).toContain('PM');
});
it('should format AM time correctly', () => {
// March 15, 2024 at 09:15:00
const timestamp = new Date(2024, 2, 15, 9, 15, 0).getTime();
const {getByText} = render(<Timestamp time={timestamp} testId="morning" />);
const formatted = getByText(/09:15 AM/);
expect(formatted).toBeTruthy();
});
it('should format PM time correctly', () => {
// June 20, 2024 at 18:45:00
const timestamp = new Date(2024, 5, 20, 18, 45, 0).getTime();
const {getByText} = render(<Timestamp time={timestamp} testId="evening" />);
const formatted = getByText(/06:45 PM/);
expect(formatted).toBeTruthy();
});
it('should handle midnight correctly', () => {
// January 1, 2024 at 00:00:00
const timestamp = new Date(2024, 0, 1, 0, 0, 0).getTime();
const {getByText} = render(
<Timestamp time={timestamp} testId="midnight" />,
);
const formatted = getByText(/12:00 AM/);
expect(formatted).toBeTruthy();
});
it('should handle noon correctly', () => {
// December 25, 2023 at 12:00:00
const timestamp = new Date(2023, 11, 25, 12, 0, 0).getTime();
const {getByText} = render(<Timestamp time={timestamp} testId="noon" />);
const formatted = getByText(/12:00 PM/);
expect(formatted).toBeTruthy();
});
it('should pad single digit minutes with zero', () => {
// April 10, 2024 at 15:05:00
const timestamp = new Date(2024, 3, 10, 15, 5, 0).getTime();
const {getByText} = render(<Timestamp time={timestamp} testId="padded" />);
const formatted = getByText(/03:05 PM/);
expect(formatted).toBeTruthy();
});
it('should render with testID prop', () => {
const timestamp = new Date(2024, 0, 1, 0, 0, 0).getTime();
const {toJSON} = render(<Timestamp time={timestamp} testId="myTest" />);
expect(toJSON()).toBeTruthy();
});
it('should display different months correctly', () => {
const months = [
{month: 0, name: 'January'},
{month: 1, name: 'February'},
{month: 2, name: 'March'},
{month: 6, name: 'July'},
{month: 11, name: 'December'},
];
months.forEach(({month, name}) => {
const timestamp = new Date(2024, month, 15, 12, 0, 0).getTime();
const {getByText} = render(
<Timestamp time={timestamp} testId="month-test" />,
);
expect(getByText(new RegExp(name))).toBeTruthy();
});
});
});

View File

@@ -0,0 +1,65 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import {ToastItem} from './ToastItem';
describe('ToastItem', () => {
it('should render toast message', () => {
const {getByText} = render(<ToastItem message="Success!" />);
expect(getByText('Success!')).toBeTruthy();
});
it('should render long message', () => {
const longMessage =
'This is a very long toast message that should still be displayed properly';
const {getByText} = render(<ToastItem message={longMessage} />);
expect(getByText(longMessage)).toBeTruthy();
});
it('should render short message', () => {
const {getByText} = render(<ToastItem message="OK" />);
expect(getByText('OK')).toBeTruthy();
});
it('should render empty message', () => {
const {toJSON} = render(<ToastItem message="" />);
expect(toJSON()).toBeTruthy();
});
it('should render message with special characters', () => {
const message = 'Error: Operation failed! (Code: 500)';
const {getByText} = render(<ToastItem message={message} />);
expect(getByText(message)).toBeTruthy();
});
it('should render message with numbers', () => {
const message = '123 items processed successfully';
const {getByText} = render(<ToastItem message={message} />);
expect(getByText(message)).toBeTruthy();
});
it('should render message with emojis', () => {
const message = '✅ Success! 🎉';
const {getByText} = render(<ToastItem message={message} />);
expect(getByText(message)).toBeTruthy();
});
it('should render multiline message', () => {
const message = 'Line 1\nLine 2\nLine 3';
const {getByText} = render(<ToastItem message={message} />);
expect(getByText(message)).toBeTruthy();
});
it('should render different toast messages', () => {
const messages = [
'Operation completed',
'Error occurred',
'Warning: Low storage',
'Info: Update available',
];
messages.forEach(message => {
const {getByText} = render(<ToastItem message={message} />);
expect(getByText(message)).toBeTruthy();
});
});
});

View File

@@ -0,0 +1,106 @@
import {DefaultTheme} from './DefaultTheme';
import {PurpleTheme} from './PurpleTheme';
describe('DefaultTheme', () => {
it('should be defined', () => {
expect(DefaultTheme).toBeDefined();
});
it('should have Colors property', () => {
expect(DefaultTheme.Colors).toBeDefined();
expect(typeof DefaultTheme.Colors).toBe('object');
});
it('should have ProfileIconColor', () => {
expect(DefaultTheme.Colors.ProfileIconColor).toBeDefined();
});
it('should have TextStyles property', () => {
expect(DefaultTheme.TextStyles).toBeDefined();
expect(typeof DefaultTheme.TextStyles).toBe('object');
});
it('should have ButtonStyles property', () => {
expect(DefaultTheme.ButtonStyles).toBeDefined();
expect(typeof DefaultTheme.ButtonStyles).toBe('object');
});
it('should have spacing function', () => {
expect(DefaultTheme.spacing).toBeDefined();
expect(typeof DefaultTheme.spacing).toBe('function');
});
it('should have elevation function', () => {
expect(DefaultTheme.elevation).toBeDefined();
expect(typeof DefaultTheme.elevation).toBe('function');
});
});
describe('PurpleTheme', () => {
it('should be defined', () => {
expect(PurpleTheme).toBeDefined();
});
it('should have Colors property', () => {
expect(PurpleTheme.Colors).toBeDefined();
expect(typeof PurpleTheme.Colors).toBe('object');
});
it('should have ProfileIconColor', () => {
expect(PurpleTheme.Colors.ProfileIconColor).toBeDefined();
});
it('should have different colors than Default', () => {
expect(PurpleTheme.Colors).not.toEqual(DefaultTheme.Colors);
});
it('should have TextStyles property', () => {
expect(PurpleTheme.TextStyles).toBeDefined();
expect(typeof PurpleTheme.TextStyles).toBe('object');
});
it('should have ButtonStyles property', () => {
expect(PurpleTheme.ButtonStyles).toBeDefined();
expect(typeof PurpleTheme.ButtonStyles).toBe('object');
});
it('should have spacing function', () => {
expect(PurpleTheme.spacing).toBeDefined();
expect(typeof PurpleTheme.spacing).toBe('function');
});
it('should have elevation function', () => {
expect(PurpleTheme.elevation).toBeDefined();
expect(typeof PurpleTheme.elevation).toBe('function');
});
it('should have same structure as DefaultTheme', () => {
const defaultKeys = Object.keys(DefaultTheme);
const purpleKeys = Object.keys(PurpleTheme);
expect(purpleKeys.sort()).toEqual(defaultKeys.sort());
});
});
describe('Theme spacing function', () => {
it('should return spacing styles for Default theme', () => {
const spacing = DefaultTheme.spacing('margin', 'xs');
expect(spacing).toBeDefined();
});
it('should return spacing styles for Purple theme', () => {
const spacing = PurpleTheme.spacing('padding', 'sm');
expect(spacing).toBeDefined();
});
});
describe('Theme elevation function', () => {
it('should return elevation styles for Default theme', () => {
const elevation = DefaultTheme.elevation(2);
expect(elevation).toBeDefined();
});
it('should return elevation styles for Purple theme', () => {
const elevation = PurpleTheme.elevation(3);
expect(elevation).toBeDefined();
});
});

View File

@@ -1,3 +1,4 @@
/* eslint-disable */
// jest.config.js
const {defaults: tsjPreset} = require('ts-jest/presets');
module.exports = {
@@ -36,8 +37,8 @@ module.exports = {
],
transformIgnorePatterns: [
'node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg|react-native-vector-icons|jsonld|jsonld-signatures|@digitalbazaar/.*)',
'node_modules/(?!(@react-native|react-native|react-native-argon2|@react-navigation|react-native-elements|react-native-size-matters|react-native-ratings|expo-constants|base58-universal|@react-native-*|react-native-google-signin|react-native-linear-gradient|expo-camera|base58-universal/*|react-native-*|react-native-vector-icons|jsonld|jsonld-signatures|@digitalbazaar/.*)/).*/',
],
'node_modules/(?!(@react-native|react-native|react-native-argon2|@react-navigation|react-native-elements|react-native-size-matters|react-native-ratings|expo-constants|base58-universal|@react-native-*|react-native-google-signin|react-native-linear-gradient|expo-camera|base58-universal/*|react-native-*|react-native-vector-icons|jsonld|jsonld-signatures|@digitalbazaar/.*)/).*/',
],
setupFiles: [
'<rootDir>/__mocks__/svg.mock.js',
'<rootDir>/__mocks__/jest-init.js',
@@ -52,8 +53,8 @@ module.exports = {
'<rootDir>/__mocks__/@noble/mock-secp256k1.js',
'<rootDir>/__mocks__/@noble/mock-ed25519.js',
'<rootDir>/__mocks__/react-native-base64.js',
'<rootDir>__mocks__/mockCrytoUtil.js',
'<rootDir>__mocks__/text-encoder.js',
'<rootDir>/__mocks__/mockCrytoUtil.js',
'<rootDir>/__mocks__/text-encoder.js',
// https://github.com/react-native-google-signin/google-signin?tab=readme-ov-file#jest-module-mock
'<rootDir>/node_modules/@react-native-google-signin/google-signin/jest/build/setup.js',
],

View File

@@ -0,0 +1,271 @@
import {IssuersEvents} from './IssuersEvents';
import {CredentialTypes} from '../VerifiableCredential/VCMetaMachine/vc';
describe('IssuersEvents', () => {
describe('SELECTED_ISSUER', () => {
it('should create event with id', () => {
const result = IssuersEvents.SELECTED_ISSUER('issuer-123');
expect(result).toEqual({id: 'issuer-123'});
});
it('should handle empty id', () => {
const result = IssuersEvents.SELECTED_ISSUER('');
expect(result).toEqual({id: ''});
});
});
describe('DOWNLOAD_ID', () => {
it('should create empty event', () => {
const result = IssuersEvents.DOWNLOAD_ID();
expect(result).toEqual({});
});
});
describe('BIOMETRIC_CANCELLED', () => {
it('should create event with requester', () => {
const result = IssuersEvents.BIOMETRIC_CANCELLED('login');
expect(result).toEqual({requester: 'login'});
});
it('should create event without requester', () => {
const result = IssuersEvents.BIOMETRIC_CANCELLED();
expect(result).toEqual({requester: undefined});
});
});
describe('COMPLETED', () => {
it('should create empty event', () => {
const result = IssuersEvents.COMPLETED();
expect(result).toEqual({});
});
});
describe('TRY_AGAIN', () => {
it('should create empty event', () => {
const result = IssuersEvents.TRY_AGAIN();
expect(result).toEqual({});
});
});
describe('RESET_ERROR', () => {
it('should create empty event', () => {
const result = IssuersEvents.RESET_ERROR();
expect(result).toEqual({});
});
});
describe('CHECK_KEY_PAIR', () => {
it('should create empty event', () => {
const result = IssuersEvents.CHECK_KEY_PAIR();
expect(result).toEqual({});
});
});
describe('CANCEL', () => {
it('should create empty event', () => {
const result = IssuersEvents.CANCEL();
expect(result).toEqual({});
});
});
describe('STORE_RESPONSE', () => {
it('should create event with response', () => {
const response = {data: 'test'};
const result = IssuersEvents.STORE_RESPONSE(response);
expect(result).toEqual({response: {data: 'test'}});
});
it('should create event without response', () => {
const result = IssuersEvents.STORE_RESPONSE();
expect(result).toEqual({response: undefined});
});
});
describe('STORE_ERROR', () => {
it('should create event with error and requester', () => {
const error = new Error('Test error');
const result = IssuersEvents.STORE_ERROR(error, 'test-requester');
expect(result.error).toBe(error);
expect(result.requester).toBe('test-requester');
});
it('should create event with error only', () => {
const error = new Error('Test error');
const result = IssuersEvents.STORE_ERROR(error);
expect(result.error).toBe(error);
expect(result.requester).toBeUndefined();
});
});
describe('RESET_VERIFY_ERROR', () => {
it('should create empty event', () => {
const result = IssuersEvents.RESET_VERIFY_ERROR();
expect(result).toEqual({});
});
});
describe('SELECTED_CREDENTIAL_TYPE', () => {
it('should create event with credential type', () => {
const credType = {} as unknown as CredentialTypes;
const result = IssuersEvents.SELECTED_CREDENTIAL_TYPE(credType);
expect(result).toEqual({credType});
});
});
describe('SCAN_CREDENTIAL_OFFER_QR_CODE', () => {
it('should create empty event', () => {
const result = IssuersEvents.SCAN_CREDENTIAL_OFFER_QR_CODE();
expect(result).toEqual({});
});
});
describe('QR_CODE_SCANNED', () => {
it('should create event with data', () => {
const result = IssuersEvents.QR_CODE_SCANNED('qr-code-data');
expect(result).toEqual({data: 'qr-code-data'});
});
it('should handle JSON data', () => {
const jsonData = '{"key": "value"}';
const result = IssuersEvents.QR_CODE_SCANNED(jsonData);
expect(result).toEqual({data: jsonData});
});
});
describe('AUTH_ENDPOINT_RECEIVED', () => {
it('should create event with auth endpoint', () => {
const result = IssuersEvents.AUTH_ENDPOINT_RECEIVED(
'https://auth.example.com',
);
expect(result).toEqual({authEndpoint: 'https://auth.example.com'});
});
});
describe('PROOF_REQUEST', () => {
it('should create event with all parameters', () => {
const accessToken = 'token-123';
const cNonce = 'nonce-456';
const issuerMetadata = {name: 'Test Issuer'};
const issuer = {id: 'issuer-1'} as unknown as any;
const credentialtypes = {} as unknown as CredentialTypes;
const result = IssuersEvents.PROOF_REQUEST(
accessToken,
cNonce,
issuerMetadata,
issuer,
credentialtypes,
);
expect(result.accessToken).toBe('token-123');
expect(result.cNonce).toBe('nonce-456');
expect(result.issuerMetadata).toEqual({name: 'Test Issuer'});
expect(result.issuer).toEqual({id: 'issuer-1'});
expect(result.credentialtypes).toBeDefined();
});
it('should handle undefined cNonce', () => {
const result = IssuersEvents.PROOF_REQUEST(
'token',
undefined,
{},
{} as unknown as any,
{} as unknown as CredentialTypes,
);
expect(result.cNonce).toBeUndefined();
});
});
describe('TX_CODE_REQUEST', () => {
it('should create empty event', () => {
const result = IssuersEvents.TX_CODE_REQUEST();
expect(result).toEqual({});
});
});
describe('TX_CODE_RECEIVED', () => {
it('should create event with txCode', () => {
const result = IssuersEvents.TX_CODE_RECEIVED('TX123456');
expect(result).toEqual({txCode: 'TX123456'});
});
it('should handle empty txCode', () => {
const result = IssuersEvents.TX_CODE_RECEIVED('');
expect(result).toEqual({txCode: ''});
});
});
describe('ON_CONSENT_GIVEN', () => {
it('should create empty event', () => {
const result = IssuersEvents.ON_CONSENT_GIVEN();
expect(result).toEqual({});
});
});
describe('TRUST_ISSUER_CONSENT_REQUEST', () => {
it('should create event with issuerMetadata', () => {
const issuerMetadata = {
name: 'Trusted Issuer',
url: 'https://issuer.example.com',
};
const result = IssuersEvents.TRUST_ISSUER_CONSENT_REQUEST(issuerMetadata);
expect(result).toEqual({issuerMetadata});
});
it('should handle empty issuerMetadata', () => {
const result = IssuersEvents.TRUST_ISSUER_CONSENT_REQUEST({});
expect(result).toEqual({issuerMetadata: {}});
});
});
describe('TOKEN_REQUEST', () => {
it('should create event with tokenRequest', () => {
const tokenRequest = {
grant_type: 'authorization_code',
code: 'auth-code-123',
};
const result = IssuersEvents.TOKEN_REQUEST(tokenRequest);
expect(result).toEqual({tokenRequest});
});
it('should handle empty tokenRequest', () => {
const result = IssuersEvents.TOKEN_REQUEST({});
expect(result).toEqual({tokenRequest: {}});
});
});
describe('IssuersEvents object structure', () => {
it('should have all expected event creators', () => {
expect(IssuersEvents.SELECTED_ISSUER).toBeDefined();
expect(IssuersEvents.DOWNLOAD_ID).toBeDefined();
expect(IssuersEvents.BIOMETRIC_CANCELLED).toBeDefined();
expect(IssuersEvents.COMPLETED).toBeDefined();
expect(IssuersEvents.TRY_AGAIN).toBeDefined();
expect(IssuersEvents.RESET_ERROR).toBeDefined();
expect(IssuersEvents.CHECK_KEY_PAIR).toBeDefined();
expect(IssuersEvents.CANCEL).toBeDefined();
expect(IssuersEvents.STORE_RESPONSE).toBeDefined();
expect(IssuersEvents.STORE_ERROR).toBeDefined();
expect(IssuersEvents.RESET_VERIFY_ERROR).toBeDefined();
expect(IssuersEvents.SELECTED_CREDENTIAL_TYPE).toBeDefined();
expect(IssuersEvents.SCAN_CREDENTIAL_OFFER_QR_CODE).toBeDefined();
expect(IssuersEvents.QR_CODE_SCANNED).toBeDefined();
expect(IssuersEvents.AUTH_ENDPOINT_RECEIVED).toBeDefined();
expect(IssuersEvents.PROOF_REQUEST).toBeDefined();
expect(IssuersEvents.TX_CODE_REQUEST).toBeDefined();
expect(IssuersEvents.TX_CODE_RECEIVED).toBeDefined();
expect(IssuersEvents.ON_CONSENT_GIVEN).toBeDefined();
expect(IssuersEvents.TRUST_ISSUER_CONSENT_REQUEST).toBeDefined();
expect(IssuersEvents.TOKEN_REQUEST).toBeDefined();
});
it('should have all event creators be functions', () => {
expect(typeof IssuersEvents.SELECTED_ISSUER).toBe('function');
expect(typeof IssuersEvents.DOWNLOAD_ID).toBe('function');
expect(typeof IssuersEvents.BIOMETRIC_CANCELLED).toBe('function');
expect(typeof IssuersEvents.COMPLETED).toBe('function');
expect(typeof IssuersEvents.TRY_AGAIN).toBe('function');
});
});
});

View File

@@ -0,0 +1,301 @@
import {IssuersModel} from './IssuersModel';
describe('IssuersModel', () => {
describe('Model structure', () => {
it('should be defined', () => {
expect(IssuersModel).toBeDefined();
});
it('should have initialContext', () => {
expect(IssuersModel.initialContext).toBeDefined();
});
it('should have events', () => {
expect(IssuersModel.events).toBeDefined();
});
});
describe('Initial Context', () => {
const initialContext = IssuersModel.initialContext;
it('should have issuers as empty array', () => {
expect(initialContext.issuers).toEqual([]);
expect(Array.isArray(initialContext.issuers)).toBe(true);
});
it('should have selectedIssuerId as empty string', () => {
expect(initialContext.selectedIssuerId).toBe('');
});
it('should have qrData as empty string', () => {
expect(initialContext.qrData).toBe('');
});
it('should have selectedIssuer as empty object', () => {
expect(initialContext.selectedIssuer).toEqual({});
});
it('should have selectedIssuerWellknownResponse as empty object', () => {
expect(initialContext.selectedIssuerWellknownResponse).toEqual({});
});
it('should have tokenResponse as empty object', () => {
expect(initialContext.tokenResponse).toEqual({});
});
it('should have errorMessage as empty string', () => {
expect(initialContext.errorMessage).toBe('');
});
it('should have loadingReason as displayIssuers', () => {
expect(initialContext.loadingReason).toBe('displayIssuers');
});
it('should have verifiableCredential as null', () => {
expect(initialContext.verifiableCredential).toBeNull();
});
it('should have selectedCredentialType as empty object', () => {
expect(initialContext.selectedCredentialType).toEqual({});
});
it('should have supportedCredentialTypes as empty array', () => {
expect(initialContext.supportedCredentialTypes).toEqual([]);
expect(Array.isArray(initialContext.supportedCredentialTypes)).toBe(true);
});
it('should have credentialWrapper as empty object', () => {
expect(initialContext.credentialWrapper).toEqual({});
});
it('should have serviceRefs as empty object', () => {
expect(initialContext.serviceRefs).toEqual({});
});
it('should have verificationErrorMessage as empty string', () => {
expect(initialContext.verificationErrorMessage).toBe('');
});
it('should have publicKey as empty string', () => {
expect(initialContext.publicKey).toBe('');
});
it('should have privateKey as empty string', () => {
expect(initialContext.privateKey).toBe('');
});
it('should have vcMetadata as empty object', () => {
expect(initialContext.vcMetadata).toEqual({});
});
it('should have keyType as RS256', () => {
expect(initialContext.keyType).toBe('RS256');
});
it('should have wellknownKeyTypes as empty array', () => {
expect(initialContext.wellknownKeyTypes).toEqual([]);
expect(Array.isArray(initialContext.wellknownKeyTypes)).toBe(true);
});
it('should have authEndpointToOpen as false', () => {
expect(initialContext.authEndpointToOpen).toBe(false);
});
it('should have isTransactionCodeRequested as false', () => {
expect(initialContext.isTransactionCodeRequested).toBe(false);
});
it('should have authEndpoint as empty string', () => {
expect(initialContext.authEndpoint).toBe('');
});
it('should have accessToken as empty string', () => {
expect(initialContext.accessToken).toBe('');
});
it('should have txCode as empty string', () => {
expect(initialContext.txCode).toBe('');
});
it('should have cNonce as empty string', () => {
expect(initialContext.cNonce).toBe('');
});
it('should have isConsentRequested as false', () => {
expect(initialContext.isConsentRequested).toBe(false);
});
it('should have issuerLogo as empty string', () => {
expect(initialContext.issuerLogo).toBe('');
});
it('should have issuerName as empty string', () => {
expect(initialContext.issuerName).toBe('');
});
it('should have txCodeInputMode as empty string', () => {
expect(initialContext.txCodeInputMode).toBe('');
});
it('should have txCodeDescription as empty string', () => {
expect(initialContext.txCodeDescription).toBe('');
});
it('should have txCodeLength as null', () => {
expect(initialContext.txCodeLength).toBeNull();
});
it('should have isCredentialOfferFlow as false', () => {
expect(initialContext.isCredentialOfferFlow).toBe(false);
});
it('should have tokenRequestObject as empty object', () => {
expect(initialContext.tokenRequestObject).toEqual({});
});
it('should have credentialConfigurationId as empty string', () => {
expect(initialContext.credentialConfigurationId).toBe('');
});
it('should have all 35 required properties', () => {
const properties = Object.keys(initialContext);
expect(properties).toHaveLength(35);
});
});
describe('String properties', () => {
const context = IssuersModel.initialContext;
it('all empty string properties should be empty', () => {
const emptyStrings = [
context.selectedIssuerId,
context.qrData,
context.errorMessage,
context.verificationErrorMessage,
context.publicKey,
context.privateKey,
context.authEndpoint,
context.accessToken,
context.txCode,
context.cNonce,
context.issuerLogo,
context.issuerName,
context.txCodeInputMode,
context.txCodeDescription,
context.credentialConfigurationId,
];
emptyStrings.forEach(str => {
expect(str).toBe('');
expect(typeof str).toBe('string');
});
});
it('loadingReason should be displayIssuers', () => {
expect(context.loadingReason).toBe('displayIssuers');
expect(typeof context.loadingReason).toBe('string');
});
it('keyType should be RS256', () => {
expect(context.keyType).toBe('RS256');
expect(typeof context.keyType).toBe('string');
});
});
describe('Array properties', () => {
const context = IssuersModel.initialContext;
it('all array properties should be empty arrays', () => {
const arrays = [
context.issuers,
context.supportedCredentialTypes,
context.wellknownKeyTypes,
];
arrays.forEach(arr => {
expect(Array.isArray(arr)).toBe(true);
expect(arr).toHaveLength(0);
});
});
});
describe('Object properties', () => {
const context = IssuersModel.initialContext;
it('all object properties should be empty objects or as specified', () => {
const emptyObjects = [
context.selectedIssuer,
context.selectedIssuerWellknownResponse,
context.tokenResponse,
context.selectedCredentialType,
context.credentialWrapper,
context.serviceRefs,
context.vcMetadata,
context.tokenRequestObject,
];
emptyObjects.forEach(obj => {
expect(typeof obj).toBe('object');
expect(Object.keys(obj)).toHaveLength(0);
});
});
});
describe('Boolean properties', () => {
const context = IssuersModel.initialContext;
it('all boolean properties should be false initially', () => {
const booleans = [
context.authEndpointToOpen,
context.isTransactionCodeRequested,
context.isConsentRequested,
context.isCredentialOfferFlow,
];
booleans.forEach(bool => {
expect(bool).toBe(false);
expect(typeof bool).toBe('boolean');
});
});
});
describe('Null properties', () => {
const context = IssuersModel.initialContext;
it('verifiableCredential should be null', () => {
expect(context.verifiableCredential).toBeNull();
});
it('txCodeLength should be null', () => {
expect(context.txCodeLength).toBeNull();
});
});
describe('Model events', () => {
it('should have events object', () => {
expect(IssuersModel.events).toBeDefined();
expect(typeof IssuersModel.events).toBe('object');
});
it('should have event creators', () => {
const eventKeys = Object.keys(IssuersModel.events);
expect(eventKeys.length).toBeGreaterThan(0);
});
});
describe('Property types', () => {
const context = IssuersModel.initialContext;
it('should have correct types for all properties', () => {
expect(typeof context.selectedIssuerId).toBe('string');
expect(typeof context.qrData).toBe('string');
expect(typeof context.errorMessage).toBe('string');
expect(typeof context.loadingReason).toBe('string');
expect(typeof context.keyType).toBe('string');
expect(typeof context.authEndpointToOpen).toBe('boolean');
expect(typeof context.isTransactionCodeRequested).toBe('boolean');
expect(Array.isArray(context.issuers)).toBe(true);
expect(typeof context.selectedIssuer).toBe('object');
});
});
});

View File

@@ -0,0 +1,372 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
selectIssuers,
selectSelectedIssuer,
selectAuthWebViewStatus,
selectAuthEndPoint,
selectErrorMessageType,
selectLoadingReason,
selectIsDownloadCredentials,
selectIsTxCodeRequested,
selectIsConsentRequested,
selectIssuerLogo,
selectIssuerName,
selectTxCodeDisplayDetails,
selectIsNonGenericError,
selectIsDone,
selectIsIdle,
selectStoring,
selectIsError,
selectVerificationErrorMessage,
selectSelectingCredentialType,
selectSupportedCredentialTypes,
selectIsQrScanning,
} from './IssuersSelectors';
describe('IssuersSelectors', () => {
const mockState: any = {
context: {
issuers: [{issuer_id: '1'}, {issuer_id: '2'}],
selectedIssuer: {issuer_id: '1', credential_issuer: 'test.example.com'},
authEndpointToOpen: 'https://auth.example.com',
authEndpoint: 'https://auth.example.com/authorize',
errorMessage: 'Test error',
loadingReason: 'Loading data',
isTransactionCodeRequested: true,
isConsentRequested: false,
issuerLogo: 'https://example.com/logo.png',
issuerName: 'Test Issuer',
txCodeInputMode: 'numeric',
txCodeDescription: 'Enter transaction code',
txCodeLength: 6,
},
matches: jest.fn(
(stateName: string) => stateName === 'downloadCredentials',
),
};
describe('selectIssuers', () => {
it('should return issuers from context', () => {
const result = selectIssuers(mockState);
expect(result).toEqual(mockState.context.issuers);
});
it('should return array of issuers', () => {
const result = selectIssuers(mockState);
expect(Array.isArray(result)).toBe(true);
expect(result.length).toBe(2);
});
});
describe('selectSelectedIssuer', () => {
it('should return selected issuer from context', () => {
const result = selectSelectedIssuer(mockState);
expect(result).toEqual(mockState.context.selectedIssuer);
});
it('should return issuer with issuer_id property', () => {
const result = selectSelectedIssuer(mockState);
expect(result.issuer_id).toBe('1');
expect(result.credential_issuer).toBe('test.example.com');
});
});
describe('selectAuthWebViewStatus', () => {
it('should return auth endpoint to open', () => {
const result = selectAuthWebViewStatus(mockState);
expect(result).toBe('https://auth.example.com');
});
});
describe('selectAuthEndPoint', () => {
it('should return auth endpoint', () => {
const result = selectAuthEndPoint(mockState);
expect(result).toBe('https://auth.example.com/authorize');
});
});
describe('selectErrorMessageType', () => {
it('should return error message from context', () => {
const result = selectErrorMessageType(mockState);
expect(result).toBe('Test error');
});
});
describe('selectLoadingReason', () => {
it('should return loading reason from context', () => {
const result = selectLoadingReason(mockState);
expect(result).toBe('Loading data');
});
});
describe('selectIsDownloadCredentials', () => {
it('should return true when in downloadCredentials state', () => {
const result = selectIsDownloadCredentials(mockState);
expect(result).toBe(true);
});
it('should call matches with correct state name', () => {
selectIsDownloadCredentials(mockState);
expect(mockState.matches).toHaveBeenCalledWith('downloadCredentials');
});
});
describe('selectIsTxCodeRequested', () => {
it('should return transaction code requested status', () => {
const result = selectIsTxCodeRequested(mockState);
expect(result).toBe(true);
});
it('should return false when not requested', () => {
const stateWithFalse: any = {
...mockState,
context: {...mockState.context, isTransactionCodeRequested: false},
};
const result = selectIsTxCodeRequested(stateWithFalse);
expect(result).toBe(false);
});
});
describe('selectIsConsentRequested', () => {
it('should return consent requested status', () => {
const result = selectIsConsentRequested(mockState);
expect(result).toBe(false);
});
it('should return true when consent requested', () => {
const stateWithConsent: any = {
...mockState,
context: {...mockState.context, isConsentRequested: true},
};
const result = selectIsConsentRequested(stateWithConsent);
expect(result).toBe(true);
});
});
describe('selectIssuerLogo', () => {
it('should return issuer logo URL', () => {
const result = selectIssuerLogo(mockState);
expect(result).toBe('https://example.com/logo.png');
});
});
describe('selectIssuerName', () => {
it('should return issuer name', () => {
const result = selectIssuerName(mockState);
expect(result).toBe('Test Issuer');
});
});
describe('selectTxCodeDisplayDetails', () => {
it('should return transaction code display details', () => {
const result = selectTxCodeDisplayDetails(mockState);
expect(result).toEqual({
inputMode: 'numeric',
description: 'Enter transaction code',
length: 6,
});
});
it('should have all required properties', () => {
const result = selectTxCodeDisplayDetails(mockState);
expect(result).toHaveProperty('inputMode');
expect(result).toHaveProperty('description');
expect(result).toHaveProperty('length');
});
it('should return correct input mode', () => {
const result = selectTxCodeDisplayDetails(mockState);
expect(result.inputMode).toBe('numeric');
});
it('should return correct description', () => {
const result = selectTxCodeDisplayDetails(mockState);
expect(result.description).toBe('Enter transaction code');
});
it('should return correct length', () => {
const result = selectTxCodeDisplayDetails(mockState);
expect(result.length).toBe(6);
});
});
describe('Selectors with empty/null values', () => {
const emptyState: any = {
context: {
issuers: [],
selectedIssuer: null,
authEndpointToOpen: '',
authEndpoint: '',
errorMessage: '',
loadingReason: '',
isTransactionCodeRequested: false,
isConsentRequested: false,
issuerLogo: '',
issuerName: '',
txCodeInputMode: '',
txCodeDescription: '',
txCodeLength: 0,
},
matches: jest.fn(() => false),
};
it('should handle empty issuers array', () => {
const result = selectIssuers(emptyState);
expect(result).toEqual([]);
});
it('should handle null selected issuer', () => {
const result = selectSelectedIssuer(emptyState);
expect(result).toBeNull();
});
it('should handle empty strings', () => {
expect(selectAuthWebViewStatus(emptyState)).toBe('');
expect(selectAuthEndPoint(emptyState)).toBe('');
expect(selectErrorMessageType(emptyState)).toBe('');
expect(selectLoadingReason(emptyState)).toBe('');
expect(selectIssuerLogo(emptyState)).toBe('');
expect(selectIssuerName(emptyState)).toBe('');
});
it('should handle false boolean values', () => {
expect(selectIsTxCodeRequested(emptyState)).toBe(false);
expect(selectIsConsentRequested(emptyState)).toBe(false);
expect(selectIsDownloadCredentials(emptyState)).toBe(false);
});
it('should handle zero length', () => {
const result = selectTxCodeDisplayDetails(emptyState);
expect(result.length).toBe(0);
});
});
describe('selectIsNonGenericError', () => {
it('should return true when error message is not generic and not empty', () => {
const stateWithError: any = {
context: {errorMessage: 'NETWORK_ERROR'},
};
const result = selectIsNonGenericError(stateWithError);
expect(result).toBe(true);
});
it('should return false when error message is GENERIC', () => {
const stateWithGenericError: any = {
context: {errorMessage: 'generic'},
};
const result = selectIsNonGenericError(stateWithGenericError);
expect(result).toBe(false);
});
it('should return false when error message is empty', () => {
const stateWithNoError: any = {
context: {errorMessage: ''},
};
const result = selectIsNonGenericError(stateWithNoError);
expect(result).toBe(false);
});
});
describe('selectIsDone', () => {
it('should return true when state matches done', () => {
const doneState: any = {
matches: jest.fn((name: string) => name === 'done'),
};
const result = selectIsDone(doneState);
expect(result).toBe(true);
expect(doneState.matches).toHaveBeenCalledWith('done');
});
it('should return false when state does not match done', () => {
const notDoneState: any = {
matches: jest.fn(() => false),
};
const result = selectIsDone(notDoneState);
expect(result).toBe(false);
});
});
describe('selectIsIdle', () => {
it('should return true when state matches idle', () => {
const idleState: any = {
matches: jest.fn((name: string) => name === 'idle'),
};
const result = selectIsIdle(idleState);
expect(result).toBe(true);
expect(idleState.matches).toHaveBeenCalledWith('idle');
});
});
describe('selectStoring', () => {
it('should return true when state matches storing', () => {
const storingState: any = {
matches: jest.fn((name: string) => name === 'storing'),
};
const result = selectStoring(storingState);
expect(result).toBe(true);
expect(storingState.matches).toHaveBeenCalledWith('storing');
});
});
describe('selectIsError', () => {
it('should return true when state matches error', () => {
const errorState: any = {
matches: jest.fn((name: string) => name === 'error'),
};
const result = selectIsError(errorState);
expect(result).toBe(true);
expect(errorState.matches).toHaveBeenCalledWith('error');
});
});
describe('selectVerificationErrorMessage', () => {
it('should return verification error message', () => {
const stateWithVerificationError: any = {
context: {verificationErrorMessage: 'Signature verification failed'},
};
const result = selectVerificationErrorMessage(stateWithVerificationError);
expect(result).toBe('Signature verification failed');
});
});
describe('selectSelectingCredentialType', () => {
it('should return true when selecting credential type', () => {
const selectingState: any = {
matches: jest.fn((name: string) => name === 'selectingCredentialType'),
};
const result = selectSelectingCredentialType(selectingState);
expect(result).toBe(true);
expect(selectingState.matches).toHaveBeenCalledWith(
'selectingCredentialType',
);
});
});
describe('selectSupportedCredentialTypes', () => {
it('should return supported credential types', () => {
const stateWithCredTypes: any = {
context: {
supportedCredentialTypes: [
{id: 'type1', name: 'Type 1'},
{id: 'type2', name: 'Type 2'},
],
},
};
const result = selectSupportedCredentialTypes(stateWithCredTypes);
expect(result).toHaveLength(2);
expect(result[0].id).toBe('type1');
expect(result[1].id).toBe('type2');
});
});
describe('selectIsQrScanning', () => {
it('should return true when waiting for QR scan', () => {
const scanningState: any = {
matches: jest.fn((name: string) => name === 'waitingForQrScan'),
};
const result = selectIsQrScanning(scanningState);
expect(result).toBe(true);
expect(scanningState.matches).toHaveBeenCalledWith('waitingForQrScan');
});
});
});

View File

@@ -0,0 +1,332 @@
import {QrLoginmodel} from './QrLoginModel';
import {VCShareFlowType} from '../../shared/Utils';
describe('QrLoginModel', () => {
describe('Model structure', () => {
it('should be defined', () => {
expect(QrLoginmodel).toBeDefined();
});
it('should have initialContext', () => {
expect(QrLoginmodel.initialContext).toBeDefined();
});
it('should have events', () => {
expect(QrLoginmodel.events).toBeDefined();
});
});
describe('Initial Context', () => {
const initialContext = QrLoginmodel.initialContext;
it('should have serviceRefs as empty object', () => {
expect(initialContext.serviceRefs).toEqual({});
expect(typeof initialContext.serviceRefs).toBe('object');
});
it('should have selectedVc as empty object', () => {
expect(initialContext.selectedVc).toEqual({});
expect(typeof initialContext.selectedVc).toBe('object');
});
it('should have linkCode as empty string', () => {
expect(initialContext.linkCode).toBe('');
expect(typeof initialContext.linkCode).toBe('string');
});
it('should have flowType as SIMPLE_SHARE', () => {
expect(initialContext.flowType).toBe(VCShareFlowType.SIMPLE_SHARE);
});
it('should have myVcs as empty array', () => {
expect(initialContext.myVcs).toEqual([]);
expect(Array.isArray(initialContext.myVcs)).toBe(true);
expect(initialContext.myVcs).toHaveLength(0);
});
it('should have thumbprint as empty string', () => {
expect(initialContext.thumbprint).toBe('');
expect(typeof initialContext.thumbprint).toBe('string');
});
it('should have linkTransactionResponse as empty object', () => {
expect(initialContext.linkTransactionResponse).toEqual({});
expect(typeof initialContext.linkTransactionResponse).toBe('object');
});
it('should have authFactors as empty array', () => {
expect(initialContext.authFactors).toEqual([]);
expect(Array.isArray(initialContext.authFactors)).toBe(true);
});
it('should have authorizeScopes as null', () => {
expect(initialContext.authorizeScopes).toBeNull();
});
it('should have clientName as empty object', () => {
expect(initialContext.clientName).toEqual({});
expect(typeof initialContext.clientName).toBe('object');
});
it('should have configs as empty object', () => {
expect(initialContext.configs).toEqual({});
expect(typeof initialContext.configs).toBe('object');
});
it('should have essentialClaims as empty array', () => {
expect(initialContext.essentialClaims).toEqual([]);
expect(Array.isArray(initialContext.essentialClaims)).toBe(true);
});
it('should have linkTransactionId as empty string', () => {
expect(initialContext.linkTransactionId).toBe('');
expect(typeof initialContext.linkTransactionId).toBe('string');
});
it('should have logoUrl as empty string', () => {
expect(initialContext.logoUrl).toBe('');
expect(typeof initialContext.logoUrl).toBe('string');
});
it('should have voluntaryClaims as empty array', () => {
expect(initialContext.voluntaryClaims).toEqual([]);
expect(Array.isArray(initialContext.voluntaryClaims)).toBe(true);
});
it('should have selectedVoluntaryClaims as empty array', () => {
expect(initialContext.selectedVoluntaryClaims).toEqual([]);
expect(Array.isArray(initialContext.selectedVoluntaryClaims)).toBe(true);
});
it('should have errorMessage as empty string', () => {
expect(initialContext.errorMessage).toBe('');
expect(typeof initialContext.errorMessage).toBe('string');
});
it('should have domainName as empty string', () => {
expect(initialContext.domainName).toBe('');
expect(typeof initialContext.domainName).toBe('string');
});
it('should have consentClaims with name and picture', () => {
expect(initialContext.consentClaims).toEqual(['name', 'picture']);
expect(Array.isArray(initialContext.consentClaims)).toBe(true);
expect(initialContext.consentClaims).toHaveLength(2);
expect(initialContext.consentClaims).toContain('name');
expect(initialContext.consentClaims).toContain('picture');
});
it('should have isSharing as empty object', () => {
expect(initialContext.isSharing).toEqual({});
expect(typeof initialContext.isSharing).toBe('object');
});
it('should have linkedTransactionId as empty string', () => {
expect(initialContext.linkedTransactionId).toBe('');
expect(typeof initialContext.linkedTransactionId).toBe('string');
});
it('should have showFaceAuthConsent as true', () => {
expect(initialContext.showFaceAuthConsent).toBe(true);
expect(typeof initialContext.showFaceAuthConsent).toBe('boolean');
});
it('should have isQrLoginViaDeepLink as false', () => {
expect(initialContext.isQrLoginViaDeepLink).toBe(false);
expect(typeof initialContext.isQrLoginViaDeepLink).toBe('boolean');
});
it('should have all required properties', () => {
const requiredProps = [
'serviceRefs',
'selectedVc',
'linkCode',
'flowType',
'myVcs',
'thumbprint',
'linkTransactionResponse',
'authFactors',
'authorizeScopes',
'clientName',
'configs',
'essentialClaims',
'linkTransactionId',
'logoUrl',
'voluntaryClaims',
'selectedVoluntaryClaims',
'errorMessage',
'domainName',
'consentClaims',
'isSharing',
'linkedTransactionId',
'showFaceAuthConsent',
'isQrLoginViaDeepLink',
];
requiredProps.forEach(prop => {
expect(initialContext).toHaveProperty(prop);
});
});
it('should have exactly 23 properties in initial context', () => {
const propertyCount = Object.keys(initialContext).length;
expect(propertyCount).toBe(23);
});
});
describe('Model events', () => {
it('should have events object defined', () => {
expect(QrLoginmodel.events).toBeDefined();
expect(typeof QrLoginmodel.events).toBe('object');
});
it('should have SELECT_VC event', () => {
expect(QrLoginmodel.events.SELECT_VC).toBeDefined();
expect(typeof QrLoginmodel.events.SELECT_VC).toBe('function');
});
it('should have SCANNING_DONE event', () => {
expect(QrLoginmodel.events.SCANNING_DONE).toBeDefined();
expect(typeof QrLoginmodel.events.SCANNING_DONE).toBe('function');
});
it('should have STORE_RESPONSE event', () => {
expect(QrLoginmodel.events.STORE_RESPONSE).toBeDefined();
expect(typeof QrLoginmodel.events.STORE_RESPONSE).toBe('function');
});
it('should have STORE_ERROR event', () => {
expect(QrLoginmodel.events.STORE_ERROR).toBeDefined();
expect(typeof QrLoginmodel.events.STORE_ERROR).toBe('function');
});
it('should have TOGGLE_CONSENT_CLAIM event', () => {
expect(QrLoginmodel.events.TOGGLE_CONSENT_CLAIM).toBeDefined();
expect(typeof QrLoginmodel.events.TOGGLE_CONSENT_CLAIM).toBe('function');
});
it('should have DISMISS event', () => {
expect(QrLoginmodel.events.DISMISS).toBeDefined();
expect(typeof QrLoginmodel.events.DISMISS).toBe('function');
});
it('should have CONFIRM event', () => {
expect(QrLoginmodel.events.CONFIRM).toBeDefined();
expect(typeof QrLoginmodel.events.CONFIRM).toBe('function');
});
it('should have GET event', () => {
expect(QrLoginmodel.events.GET).toBeDefined();
expect(typeof QrLoginmodel.events.GET).toBe('function');
});
it('should have VERIFY event', () => {
expect(QrLoginmodel.events.VERIFY).toBeDefined();
expect(typeof QrLoginmodel.events.VERIFY).toBe('function');
});
it('should have CANCEL event', () => {
expect(QrLoginmodel.events.CANCEL).toBeDefined();
expect(typeof QrLoginmodel.events.CANCEL).toBe('function');
});
it('should have FACE_VALID event', () => {
expect(QrLoginmodel.events.FACE_VALID).toBeDefined();
expect(typeof QrLoginmodel.events.FACE_VALID).toBe('function');
});
it('should have FACE_INVALID event', () => {
expect(QrLoginmodel.events.FACE_INVALID).toBeDefined();
expect(typeof QrLoginmodel.events.FACE_INVALID).toBe('function');
});
it('should have RETRY_VERIFICATION event', () => {
expect(QrLoginmodel.events.RETRY_VERIFICATION).toBeDefined();
expect(typeof QrLoginmodel.events.RETRY_VERIFICATION).toBe('function');
});
it('should have FACE_VERIFICATION_CONSENT event', () => {
expect(QrLoginmodel.events.FACE_VERIFICATION_CONSENT).toBeDefined();
expect(typeof QrLoginmodel.events.FACE_VERIFICATION_CONSENT).toBe(
'function',
);
});
});
describe('String properties', () => {
const context = QrLoginmodel.initialContext;
it('all string properties should be empty strings initially', () => {
const stringProps = [
context.linkCode,
context.thumbprint,
context.linkTransactionId,
context.logoUrl,
context.errorMessage,
context.domainName,
context.linkedTransactionId,
];
stringProps.forEach(prop => {
expect(prop).toBe('');
expect(typeof prop).toBe('string');
});
});
});
describe('Array properties', () => {
const context = QrLoginmodel.initialContext;
it('empty array properties should have length 0', () => {
const emptyArrays = [
context.myVcs,
context.authFactors,
context.essentialClaims,
context.voluntaryClaims,
context.selectedVoluntaryClaims,
];
emptyArrays.forEach(arr => {
expect(Array.isArray(arr)).toBe(true);
expect(arr).toHaveLength(0);
});
});
it('consentClaims should have length 2', () => {
expect(context.consentClaims).toHaveLength(2);
expect(Array.isArray(context.consentClaims)).toBe(true);
});
});
describe('Object properties', () => {
const context = QrLoginmodel.initialContext;
it('empty object properties should have no keys', () => {
const emptyObjects = [
context.serviceRefs,
context.selectedVc,
context.linkTransactionResponse,
context.clientName,
context.configs,
context.isSharing,
];
emptyObjects.forEach(obj => {
expect(typeof obj).toBe('object');
expect(Object.keys(obj)).toHaveLength(0);
});
});
});
describe('Boolean properties', () => {
const context = QrLoginmodel.initialContext;
it('showFaceAuthConsent should be true', () => {
expect(context.showFaceAuthConsent).toBe(true);
});
it('isQrLoginViaDeepLink should be false', () => {
expect(context.isQrLoginViaDeepLink).toBe(false);
});
});
});

View File

@@ -0,0 +1,512 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
selectMyVcs,
selectIsWaitingForData,
selectDomainName,
selectIsLinkTransaction,
selectIsloadMyVcs,
selectIsShowingVcList,
selectIsisVerifyingIdentity,
selectIsInvalidIdentity,
selectIsShowError,
selectIsRequestConsent,
selectIsSendingAuthenticate,
selectIsSendingConsent,
selectIsVerifyingSuccesful,
selectCredential,
selectVerifiableCredentialData,
selectLinkTransactionResponse,
selectEssentialClaims,
selectVoluntaryClaims,
selectLogoUrl,
selectClientName,
selectErrorMessage,
selectIsSharing,
selectIsQrLoginViaDeepLink,
selectIsFaceVerificationConsent,
} from './QrLoginSelectors';
describe('QrLoginSelectors', () => {
const mockVc = {
vcMetadata: {id: 'vc1', displayName: 'Test VC'},
credential: {},
};
const mockState: any = {
context: {
myVcs: [mockVc],
domainName: 'example.com',
selectedVc: mockVc,
senderInfo: {name: 'Test Sender'},
linkTransactionResponse: {status: 'success'},
verifiableCredentialData: {type: 'VerifiableCredential'},
connectionParams: {uri: 'https://connect.example.com'},
},
matches: jest.fn((stateName: string) => stateName === 'waitingForData'),
};
describe('selectMyVcs', () => {
it('should return my VCs from context', () => {
const result = selectMyVcs(mockState);
expect(result).toEqual([mockVc]);
});
it('should return array of VCs', () => {
const result = selectMyVcs(mockState);
expect(Array.isArray(result)).toBe(true);
expect(result.length).toBe(1);
});
});
describe('selectIsWaitingForData', () => {
it('should return true when in waitingForData state', () => {
const result = selectIsWaitingForData(mockState);
expect(result).toBe(true);
});
it('should call matches with correct state', () => {
selectIsWaitingForData(mockState);
expect(mockState.matches).toHaveBeenCalledWith('waitingForData');
});
it('should return false when not in waitingForData state', () => {
const state: any = {
...mockState,
matches: jest.fn(() => false),
};
const result = selectIsWaitingForData(state);
expect(result).toBe(false);
});
});
describe('selectDomainName', () => {
it('should return domain name from context', () => {
const result = selectDomainName(mockState);
expect(result).toBe('example.com');
});
});
describe('selectIsLinkTransaction', () => {
it('should return true when in linkTransaction state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'linkTransaction'),
};
const result = selectIsLinkTransaction(state);
expect(result).toBe(true);
});
it('should call matches with linkTransaction', () => {
const state: any = {
...mockState,
matches: jest.fn(() => false),
};
selectIsLinkTransaction(state);
expect(state.matches).toHaveBeenCalledWith('linkTransaction');
});
});
describe('selectIsloadMyVcs', () => {
it('should return true when in loadMyVcs state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'loadMyVcs'),
};
const result = selectIsloadMyVcs(state);
expect(result).toBe(true);
});
it('should call matches with loadMyVcs', () => {
const state: any = {
...mockState,
matches: jest.fn(() => false),
};
selectIsloadMyVcs(state);
expect(state.matches).toHaveBeenCalledWith('loadMyVcs');
});
});
describe('selectIsShowingVcList', () => {
it('should return true when in showvcList state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'showvcList'),
};
const result = selectIsShowingVcList(state);
expect(result).toBe(true);
});
it('should call matches with showvcList', () => {
const state: any = {
...mockState,
matches: jest.fn(() => false),
};
selectIsShowingVcList(state);
expect(state.matches).toHaveBeenCalledWith('showvcList');
});
});
describe('selectIsisVerifyingIdentity', () => {
it('should return true when in faceAuth state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'faceAuth'),
};
const result = selectIsisVerifyingIdentity(state);
expect(result).toBe(true);
});
it('should call matches with faceAuth', () => {
const state: any = {
...mockState,
matches: jest.fn(() => false),
};
selectIsisVerifyingIdentity(state);
expect(state.matches).toHaveBeenCalledWith('faceAuth');
});
});
describe('selectIsInvalidIdentity', () => {
it('should return true when in invalidIdentity state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'invalidIdentity'),
};
const result = selectIsInvalidIdentity(state);
expect(result).toBe(true);
});
it('should call matches with invalidIdentity', () => {
const state: any = {
...mockState,
matches: jest.fn(() => false),
};
selectIsInvalidIdentity(state);
expect(state.matches).toHaveBeenCalledWith('invalidIdentity');
});
});
describe('selectIsShowError', () => {
it('should return true when in ShowError state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'ShowError'),
};
const result = selectIsShowError(state);
expect(result).toBe(true);
});
it('should call matches with ShowError', () => {
const state: any = {
...mockState,
matches: jest.fn(() => false),
};
selectIsShowError(state);
expect(state.matches).toHaveBeenCalledWith('ShowError');
});
});
describe('selectIsRequestConsent', () => {
it('should return true when in requestConsent state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'requestConsent'),
};
const result = selectIsRequestConsent(state);
expect(result).toBe(true);
});
it('should call matches with requestConsent', () => {
const state: any = {
...mockState,
matches: jest.fn(() => false),
};
selectIsRequestConsent(state);
expect(state.matches).toHaveBeenCalledWith('requestConsent');
});
});
describe('selectIsSendingAuthenticate', () => {
it('should return true when in sendingAuthenticate state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'sendingAuthenticate'),
};
const result = selectIsSendingAuthenticate(state);
expect(result).toBe(true);
});
it('should call matches with sendingAuthenticate', () => {
const state: any = {
...mockState,
matches: jest.fn(() => false),
};
selectIsSendingAuthenticate(state);
expect(state.matches).toHaveBeenCalledWith('sendingAuthenticate');
});
});
describe('selectIsSendingConsent', () => {
it('should return true when in sendingConsent state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'sendingConsent'),
};
const result = selectIsSendingConsent(state);
expect(result).toBe(true);
});
it('should call matches with sendingConsent', () => {
const state: any = {
...mockState,
matches: jest.fn(() => false),
};
selectIsSendingConsent(state);
expect(state.matches).toHaveBeenCalledWith('sendingConsent');
});
});
describe('selectIsVerifyingSuccesful', () => {
it('should return true when in success state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'success'),
};
const result = selectIsVerifyingSuccesful(state);
expect(result).toBe(true);
});
it('should call matches with success', () => {
const state: any = {
...mockState,
matches: jest.fn(() => false),
};
selectIsVerifyingSuccesful(state);
expect(state.matches).toHaveBeenCalledWith('success');
});
});
describe('selectCredential', () => {
it('should return credential from selectedVc', () => {
const result = selectCredential(mockState);
expect(result).toBeDefined();
});
it('should handle null selectedVc', () => {
const state: any = {
...mockState,
context: {...mockState.context, selectedVc: null},
};
const result = selectCredential(state);
expect(result).toBeUndefined();
});
});
describe('selectVerifiableCredentialData', () => {
it('should return verifiable credential data', () => {
const result = selectVerifiableCredentialData(mockState);
expect(Array.isArray(result)).toBe(true);
expect(result.length).toBe(1);
});
it('should include vcMetadata, issuer, and issuerLogo', () => {
const result = selectVerifiableCredentialData(mockState);
expect(result[0]).toHaveProperty('vcMetadata');
expect(result[0]).toHaveProperty('issuer');
expect(result[0]).toHaveProperty('issuerLogo');
});
});
describe('selectLinkTransactionResponse', () => {
it('should return link transaction response from context', () => {
const result = selectLinkTransactionResponse(mockState);
expect(result).toEqual({status: 'success'});
});
});
describe('selectEssentialClaims', () => {
const stateWithClaims: any = {
...mockState,
context: {...mockState.context, essentialClaims: ['name', 'age']},
};
it('should return essential claims from context', () => {
const result = selectEssentialClaims(stateWithClaims);
expect(result).toEqual(['name', 'age']);
});
});
describe('selectVoluntaryClaims', () => {
const stateWithClaims: any = {
...mockState,
context: {...mockState.context, voluntaryClaims: ['email']},
};
it('should return voluntary claims from context', () => {
const result = selectVoluntaryClaims(stateWithClaims);
expect(result).toEqual(['email']);
});
});
describe('selectLogoUrl', () => {
const stateWithLogo: any = {
...mockState,
context: {...mockState.context, logoUrl: 'https://example.com/logo.png'},
};
it('should return logo URL from context', () => {
const result = selectLogoUrl(stateWithLogo);
expect(result).toBe('https://example.com/logo.png');
});
});
describe('selectClientName', () => {
const stateWithClient: any = {
...mockState,
context: {...mockState.context, clientName: 'Test Client'},
};
it('should return client name from context', () => {
const result = selectClientName(stateWithClient);
expect(result).toBe('Test Client');
});
});
describe('selectErrorMessage', () => {
const stateWithError: any = {
...mockState,
context: {...mockState.context, errorMessage: 'Test error'},
};
it('should return error message from context', () => {
const result = selectErrorMessage(stateWithError);
expect(result).toBe('Test error');
});
});
describe('selectIsSharing', () => {
const stateWithSharing: any = {
...mockState,
context: {...mockState.context, isSharing: true},
};
it('should return sharing status from context', () => {
const result = selectIsSharing(stateWithSharing);
expect(result).toBe(true);
});
it('should return false when not sharing', () => {
const state: any = {
...mockState,
context: {...mockState.context, isSharing: false},
};
const result = selectIsSharing(state);
expect(result).toBe(false);
});
});
describe('selectIsQrLoginViaDeepLink', () => {
const stateWithDeepLink: any = {
...mockState,
context: {...mockState.context, isQrLoginViaDeepLink: true},
};
it('should return deep link status from context', () => {
const result = selectIsQrLoginViaDeepLink(stateWithDeepLink);
expect(result).toBe(true);
});
it('should return false when not via deep link', () => {
const state: any = {
...mockState,
context: {...mockState.context, isQrLoginViaDeepLink: false},
};
const result = selectIsQrLoginViaDeepLink(state);
expect(result).toBe(false);
});
});
describe('selectIsFaceVerificationConsent', () => {
it('should return true when in faceVerificationConsent state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'faceVerificationConsent'),
};
const result = selectIsFaceVerificationConsent(state);
expect(result).toBe(true);
});
it('should call matches with faceVerificationConsent', () => {
const state: any = {
...mockState,
matches: jest.fn(() => false),
};
selectIsFaceVerificationConsent(state);
expect(state.matches).toHaveBeenCalledWith('faceVerificationConsent');
});
});
describe('Selectors with empty/null values', () => {
const emptyState: any = {
context: {
myVcs: [],
domainName: '',
selectedVc: null,
linkTransactionResponse: null,
essentialClaims: [],
voluntaryClaims: [],
logoUrl: '',
clientName: '',
errorMessage: '',
isSharing: false,
isQrLoginViaDeepLink: false,
},
matches: jest.fn(() => false),
};
it('should handle empty VCs array', () => {
const result = selectMyVcs(emptyState);
expect(result).toEqual([]);
});
it('should handle empty domain name', () => {
const result = selectDomainName(emptyState);
expect(result).toBe('');
});
it('should handle empty strings', () => {
expect(selectLogoUrl(emptyState)).toBe('');
expect(selectClientName(emptyState)).toBe('');
expect(selectErrorMessage(emptyState)).toBe('');
});
it('should handle null responses', () => {
expect(selectLinkTransactionResponse(emptyState)).toBeNull();
});
it('should handle empty arrays', () => {
expect(selectEssentialClaims(emptyState)).toEqual([]);
expect(selectVoluntaryClaims(emptyState)).toEqual([]);
});
it('should handle false boolean values', () => {
expect(selectIsSharing(emptyState)).toBe(false);
expect(selectIsQrLoginViaDeepLink(emptyState)).toBe(false);
});
it('should return false for all state checks', () => {
expect(selectIsWaitingForData(emptyState)).toBe(false);
expect(selectIsLinkTransaction(emptyState)).toBe(false);
expect(selectIsloadMyVcs(emptyState)).toBe(false);
expect(selectIsShowingVcList(emptyState)).toBe(false);
expect(selectIsisVerifyingIdentity(emptyState)).toBe(false);
expect(selectIsInvalidIdentity(emptyState)).toBe(false);
expect(selectIsShowError(emptyState)).toBe(false);
expect(selectIsRequestConsent(emptyState)).toBe(false);
expect(selectIsSendingAuthenticate(emptyState)).toBe(false);
expect(selectIsSendingConsent(emptyState)).toBe(false);
expect(selectIsVerifyingSuccesful(emptyState)).toBe(false);
expect(selectIsFaceVerificationConsent(emptyState)).toBe(false);
});
});
});

View File

@@ -0,0 +1,290 @@
import {VCItemModel} from './VCItemModel';
describe('VCItemModel', () => {
describe('Model structure', () => {
it('should be defined', () => {
expect(VCItemModel).toBeDefined();
});
it('should have initialContext', () => {
expect(VCItemModel.initialContext).toBeDefined();
});
it('should have events', () => {
expect(VCItemModel.events).toBeDefined();
});
});
describe('Initial Context', () => {
const initialContext = VCItemModel.initialContext;
it('should have serviceRefs as empty object', () => {
expect(initialContext.serviceRefs).toEqual({});
expect(typeof initialContext.serviceRefs).toBe('object');
});
it('should have vcMetadata as empty object', () => {
expect(initialContext.vcMetadata).toEqual({});
expect(typeof initialContext.vcMetadata).toBe('object');
});
it('should have generatedOn as Date instance', () => {
expect(initialContext.generatedOn).toBeInstanceOf(Date);
});
it('should have credential as null', () => {
expect(initialContext.credential).toBeNull();
});
it('should have verifiableCredential as null', () => {
expect(initialContext.verifiableCredential).toBeNull();
});
it('should have hashedId as empty string', () => {
expect(initialContext.hashedId).toBe('');
expect(typeof initialContext.hashedId).toBe('string');
});
it('should have publicKey as empty string', () => {
expect(initialContext.publicKey).toBe('');
expect(typeof initialContext.publicKey).toBe('string');
});
it('should have privateKey as empty string', () => {
expect(initialContext.privateKey).toBe('');
expect(typeof initialContext.privateKey).toBe('string');
});
it('should have OTP as empty string', () => {
expect(initialContext.OTP).toBe('');
expect(typeof initialContext.OTP).toBe('string');
});
it('should have error as empty string', () => {
expect(initialContext.error).toBe('');
expect(typeof initialContext.error).toBe('string');
});
it('should have bindingTransactionId as empty string', () => {
expect(initialContext.bindingTransactionId).toBe('');
expect(typeof initialContext.bindingTransactionId).toBe('string');
});
it('should have requestId as empty string', () => {
expect(initialContext.requestId).toBe('');
expect(typeof initialContext.requestId).toBe('string');
});
it('should have downloadCounter as 0', () => {
expect(initialContext.downloadCounter).toBe(0);
expect(typeof initialContext.downloadCounter).toBe('number');
});
it('should have maxDownloadCount as null', () => {
expect(initialContext.maxDownloadCount).toBeNull();
});
it('should have downloadInterval as null', () => {
expect(initialContext.downloadInterval).toBeNull();
});
it('should have walletBindingResponse as null', () => {
expect(initialContext.walletBindingResponse).toBeNull();
});
it('should have isMachineInKebabPopupState as false', () => {
expect(initialContext.isMachineInKebabPopupState).toBe(false);
expect(typeof initialContext.isMachineInKebabPopupState).toBe('boolean');
});
it('should have communicationDetails as null', () => {
expect(initialContext.communicationDetails).toBeNull();
});
it('should have verificationStatus as null', () => {
expect(initialContext.verificationStatus).toBeNull();
});
it('should have showVerificationStatusBanner as false', () => {
expect(initialContext.showVerificationStatusBanner).toBe(false);
expect(typeof initialContext.showVerificationStatusBanner).toBe(
'boolean',
);
});
it('should have wellknownResponse as empty object', () => {
expect(initialContext.wellknownResponse).toEqual({});
expect(typeof initialContext.wellknownResponse).toBe('object');
});
it('should have all 24 required properties', () => {
const properties = Object.keys(initialContext);
expect(properties).toHaveLength(24);
});
});
describe('String properties', () => {
const context = VCItemModel.initialContext;
it('all empty string properties should be empty', () => {
const emptyStrings = [
context.hashedId,
context.publicKey,
context.privateKey,
context.OTP,
context.error,
context.bindingTransactionId,
context.requestId,
];
emptyStrings.forEach(str => {
expect(str).toBe('');
expect(typeof str).toBe('string');
});
});
});
describe('Object properties', () => {
const context = VCItemModel.initialContext;
it('empty object properties should be empty objects', () => {
const emptyObjects = [
context.serviceRefs,
context.vcMetadata,
context.wellknownResponse,
];
emptyObjects.forEach(obj => {
expect(typeof obj).toBe('object');
expect(Object.keys(obj)).toHaveLength(0);
});
});
});
describe('Boolean properties', () => {
const context = VCItemModel.initialContext;
it('isMachineInKebabPopupState should be false', () => {
expect(context.isMachineInKebabPopupState).toBe(false);
expect(typeof context.isMachineInKebabPopupState).toBe('boolean');
});
it('showVerificationStatusBanner should be false', () => {
expect(context.showVerificationStatusBanner).toBe(false);
expect(typeof context.showVerificationStatusBanner).toBe('boolean');
});
it('all boolean properties should be false initially', () => {
const booleans = [
context.isMachineInKebabPopupState,
context.showVerificationStatusBanner,
];
booleans.forEach(bool => {
expect(bool).toBe(false);
expect(typeof bool).toBe('boolean');
});
});
});
describe('Null properties', () => {
const context = VCItemModel.initialContext;
it('credential should be null', () => {
expect(context.credential).toBeNull();
});
it('verifiableCredential should be null', () => {
expect(context.verifiableCredential).toBeNull();
});
it('maxDownloadCount should be null', () => {
expect(context.maxDownloadCount).toBeNull();
});
it('downloadInterval should be null', () => {
expect(context.downloadInterval).toBeNull();
});
it('walletBindingResponse should be null', () => {
expect(context.walletBindingResponse).toBeNull();
});
it('communicationDetails should be null', () => {
expect(context.communicationDetails).toBeNull();
});
it('verificationStatus should be null', () => {
expect(context.verificationStatus).toBeNull();
});
it('all null properties should be null initially', () => {
const nullProps = [
context.credential,
context.verifiableCredential,
context.maxDownloadCount,
context.downloadInterval,
context.walletBindingResponse,
context.communicationDetails,
context.verificationStatus,
];
nullProps.forEach(prop => {
expect(prop).toBeNull();
});
});
});
describe('Number properties', () => {
const context = VCItemModel.initialContext;
it('downloadCounter should be 0', () => {
expect(context.downloadCounter).toBe(0);
expect(typeof context.downloadCounter).toBe('number');
});
});
describe('Date properties', () => {
const context = VCItemModel.initialContext;
it('generatedOn should be a Date instance', () => {
expect(context.generatedOn).toBeInstanceOf(Date);
});
it('generatedOn should be a valid date', () => {
expect(context.generatedOn.getTime()).not.toBeNaN();
});
});
describe('Model events', () => {
it('should have events object', () => {
expect(VCItemModel.events).toBeDefined();
expect(typeof VCItemModel.events).toBe('object');
});
it('should have event creators', () => {
const eventKeys = Object.keys(VCItemModel.events);
expect(eventKeys.length).toBeGreaterThan(0);
});
});
describe('Property types validation', () => {
const context = VCItemModel.initialContext;
it('should have correct types for all properties', () => {
expect(typeof context.hashedId).toBe('string');
expect(typeof context.publicKey).toBe('string');
expect(typeof context.privateKey).toBe('string');
expect(typeof context.OTP).toBe('string');
expect(typeof context.error).toBe('string');
expect(typeof context.bindingTransactionId).toBe('string');
expect(typeof context.requestId).toBe('string');
expect(typeof context.downloadCounter).toBe('number');
expect(typeof context.isMachineInKebabPopupState).toBe('boolean');
expect(typeof context.showVerificationStatusBanner).toBe('boolean');
expect(typeof context.serviceRefs).toBe('object');
expect(typeof context.vcMetadata).toBe('object');
expect(context.generatedOn).toBeInstanceOf(Date);
});
});
});

View File

@@ -0,0 +1,451 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
selectVerificationStatus,
selectIsVerificationInProgress,
selectIsVerificationCompleted,
selectShowVerificationStatusBanner,
selectVerifiableCredential,
getVerifiableCredential,
selectCredential,
selectVerifiableCredentialData,
selectKebabPopUp,
selectContext,
selectGeneratedOn,
selectWalletBindingSuccess,
selectWalletBindingResponse,
selectIsCommunicationDetails,
selectWalletBindingError,
selectBindingAuthFailedError,
selectAcceptingBindingOtp,
selectWalletBindingInProgress,
selectBindingWarning,
selectRemoveWalletWarning,
selectIsPinned,
selectOtpError,
selectShowActivities,
selectShowWalletBindingError,
selectVc,
} from './VCItemSelectors';
describe('VCItemSelectors', () => {
describe('selectVerificationStatus', () => {
it('should return verification status from context', () => {
const mockState: any = {
context: {
verificationStatus: 'verified',
},
};
expect(selectVerificationStatus(mockState)).toBe('verified');
});
});
describe('selectIsVerificationInProgress', () => {
it('should return true when in verifyingCredential state', () => {
const mockState: any = {
matches: jest.fn(
(state: string) => state === 'verifyState.verifyingCredential',
),
};
expect(selectIsVerificationInProgress(mockState)).toBe(true);
});
it('should return false when not in verifyingCredential state', () => {
const mockState: any = {
matches: jest.fn(() => false),
};
expect(selectIsVerificationInProgress(mockState)).toBe(false);
});
});
describe('selectIsVerificationCompleted', () => {
it('should return true when in verificationCompleted state', () => {
const mockState: any = {
matches: jest.fn(
(state: string) => state === 'verifyState.verificationCompleted',
),
};
expect(selectIsVerificationCompleted(mockState)).toBe(true);
});
it('should return false when not in verificationCompleted state', () => {
const mockState: any = {
matches: jest.fn(() => false),
};
expect(selectIsVerificationCompleted(mockState)).toBe(false);
});
});
describe('selectShowVerificationStatusBanner', () => {
it('should return showVerificationStatusBanner from context', () => {
const mockState: any = {
context: {
showVerificationStatusBanner: true,
},
};
expect(selectShowVerificationStatusBanner(mockState)).toBe(true);
});
});
describe('selectVerifiableCredential', () => {
it('should return verifiableCredential from context', () => {
const mockVC = {credential: {id: 'test-123'}};
const mockState: any = {
context: {
verifiableCredential: mockVC,
},
};
expect(selectVerifiableCredential(mockState)).toBe(mockVC);
});
});
describe('getVerifiableCredential', () => {
it('should return credential property if it exists', () => {
const mockCredential = {id: 'cred-123'};
const mockVC: any = {
credential: mockCredential,
};
expect(getVerifiableCredential(mockVC)).toBe(mockCredential);
});
it('should return the whole object if credential property does not exist', () => {
const mockCredential: any = {id: 'cred-456'};
expect(getVerifiableCredential(mockCredential)).toBe(mockCredential);
});
});
describe('selectCredential', () => {
it('should return verifiableCredential from context', () => {
const mockVC = {credential: {id: 'test-789'}};
const mockState: any = {
context: {
verifiableCredential: mockVC,
},
};
expect(selectCredential(mockState)).toBe(mockVC);
});
});
describe('selectVerifiableCredentialData', () => {
it('should return formatted verifiable credential data', () => {
const mockState: any = {
context: {
vcMetadata: {
id: 'vc-001',
issuer: 'Test Issuer',
format: 'ldp_vc',
},
verifiableCredential: {
credential: {
credentialSubject: {
name: 'John Doe',
},
},
issuerLogo: 'https://example.com/logo.png',
wellKnown: 'https://example.com/.well-known',
credentialConfigurationId: 'config-123',
},
format: 'ldp_vc',
credential: null,
},
};
const result = selectVerifiableCredentialData(mockState);
expect(result.issuer).toBe('Test Issuer');
expect(result.issuerLogo).toBe('https://example.com/logo.png');
expect(result.wellKnown).toBe('https://example.com/.well-known');
expect(result.credentialConfigurationId).toBe('config-123');
});
});
describe('selectKebabPopUp', () => {
it('should return isMachineInKebabPopupState from context', () => {
const mockState: any = {
context: {
isMachineInKebabPopupState: true,
},
};
expect(selectKebabPopUp(mockState)).toBe(true);
});
});
describe('selectContext', () => {
it('should return entire context', () => {
const mockContext = {
verificationStatus: 'verified',
generatedOn: '2023-01-01',
};
const mockState: any = {
context: mockContext,
};
expect(selectContext(mockState)).toBe(mockContext);
});
});
describe('selectGeneratedOn', () => {
it('should return generatedOn from context', () => {
const mockState: any = {
context: {
generatedOn: '2023-12-25',
},
};
expect(selectGeneratedOn(mockState)).toBe('2023-12-25');
});
});
describe('selectWalletBindingSuccess', () => {
it('should return walletBindingResponse from context', () => {
const mockResponse = {walletBindingId: 'binding-123'};
const mockState: any = {
context: {
walletBindingResponse: mockResponse,
},
};
expect(selectWalletBindingSuccess(mockState)).toBe(mockResponse);
});
});
describe('selectWalletBindingResponse', () => {
it('should return walletBindingResponse from context', () => {
const mockResponse = {walletBindingId: 'binding-456'};
const mockState: any = {
context: {
walletBindingResponse: mockResponse,
},
};
expect(selectWalletBindingResponse(mockState)).toBe(mockResponse);
});
});
describe('selectIsCommunicationDetails', () => {
it('should return communicationDetails from context', () => {
const mockDetails = {email: 'test@example.com', phone: '1234567890'};
const mockState: any = {
context: {
communicationDetails: mockDetails,
},
};
expect(selectIsCommunicationDetails(mockState)).toBe(mockDetails);
});
});
describe('selectWalletBindingError', () => {
it('should return error from context', () => {
const mockError = new Error('Binding failed');
const mockState: any = {
context: {
error: mockError,
},
};
expect(selectWalletBindingError(mockState)).toBe(mockError);
});
});
describe('selectBindingAuthFailedError', () => {
it('should return error from context', () => {
const mockError = new Error('Auth failed');
const mockState: any = {
context: {
error: mockError,
},
};
expect(selectBindingAuthFailedError(mockState)).toBe(mockError);
});
});
describe('selectAcceptingBindingOtp', () => {
it('should return true when in acceptingBindingOTP state', () => {
const mockState: any = {
matches: jest.fn(
(state: string) =>
state === 'vcUtilitiesState.walletBinding.acceptingBindingOTP',
),
};
expect(selectAcceptingBindingOtp(mockState)).toBe(true);
});
it('should return false when not in acceptingBindingOTP state', () => {
const mockState: any = {
matches: jest.fn(() => false),
};
expect(selectAcceptingBindingOtp(mockState)).toBe(false);
});
});
describe('selectWalletBindingInProgress', () => {
it('should return true when in requestingBindingOTP state', () => {
const mockState: any = {
matches: jest.fn(
(state: string) =>
state === 'vcUtilitiesState.walletBinding.requestingBindingOTP',
),
};
expect(selectWalletBindingInProgress(mockState)).toBe(true);
});
it('should return true when in addingWalletBindingId state', () => {
const mockState: any = {
matches: jest.fn(
(state: string) =>
state === 'vcUtilitiesState.walletBinding.addingWalletBindingId',
),
};
expect(selectWalletBindingInProgress(mockState)).toBe(true);
});
it('should return true when in addKeyPair state', () => {
const mockState: any = {
matches: jest.fn(
(state: string) =>
state === 'vcUtilitiesState.walletBinding.addKeyPair',
),
};
expect(selectWalletBindingInProgress(mockState)).toBe(true);
});
it('should return true when in updatingPrivateKey state', () => {
const mockState: any = {
matches: jest.fn(
(state: string) =>
state === 'vcUtilitiesState.walletBinding.updatingPrivateKey',
),
};
expect(selectWalletBindingInProgress(mockState)).toBe(true);
});
it('should return false when not in any wallet binding progress state', () => {
const mockState: any = {
matches: jest.fn(() => false),
};
expect(selectWalletBindingInProgress(mockState)).toBe(false);
});
});
describe('selectBindingWarning', () => {
it('should return true when in showBindingWarning state', () => {
const mockState: any = {
matches: jest.fn(
(state: string) =>
state === 'vcUtilitiesState.walletBinding.showBindingWarning',
),
};
expect(selectBindingWarning(mockState)).toBe(true);
});
it('should return false when not in showBindingWarning state', () => {
const mockState: any = {
matches: jest.fn(() => false),
};
expect(selectBindingWarning(mockState)).toBe(false);
});
});
describe('selectRemoveWalletWarning', () => {
it('should return true when in removeWallet state', () => {
const mockState: any = {
matches: jest.fn(
(state: string) =>
state === 'vcUtilitiesState.kebabPopUp.removeWallet',
),
};
expect(selectRemoveWalletWarning(mockState)).toBe(true);
});
it('should return false when not in removeWallet state', () => {
const mockState: any = {
matches: jest.fn(() => false),
};
expect(selectRemoveWalletWarning(mockState)).toBe(false);
});
});
describe('selectIsPinned', () => {
it('should return isPinned from vcMetadata', () => {
const mockState: any = {
context: {
vcMetadata: {
isPinned: true,
},
},
};
expect(selectIsPinned(mockState)).toBe(true);
});
it('should return false when isPinned is false', () => {
const mockState: any = {
context: {
vcMetadata: {
isPinned: false,
},
},
};
expect(selectIsPinned(mockState)).toBe(false);
});
});
describe('selectOtpError', () => {
it('should return error from context', () => {
const mockError = new Error('OTP invalid');
const mockState: any = {
context: {
error: mockError,
},
};
expect(selectOtpError(mockState)).toBe(mockError);
});
});
describe('selectShowActivities', () => {
it('should return true when in showActivities state', () => {
const mockState: any = {
matches: jest.fn(
(state: string) =>
state === 'vcUtilitiesState.kebabPopUp.showActivities',
),
};
expect(selectShowActivities(mockState)).toBe(true);
});
it('should return false when not in showActivities state', () => {
const mockState: any = {
matches: jest.fn(() => false),
};
expect(selectShowActivities(mockState)).toBe(false);
});
});
describe('selectShowWalletBindingError', () => {
it('should return true when in showingWalletBindingError state', () => {
const mockState: any = {
matches: jest.fn(
(state: string) =>
state ===
'vcUtilitiesState.walletBinding.showingWalletBindingError',
),
};
expect(selectShowWalletBindingError(mockState)).toBe(true);
});
it('should return false when not in showingWalletBindingError state', () => {
const mockState: any = {
matches: jest.fn(() => false),
};
expect(selectShowWalletBindingError(mockState)).toBe(false);
});
});
describe('selectVc', () => {
it('should return context without serviceRefs', () => {
const mockState: any = {
context: {
verificationStatus: 'verified',
generatedOn: '2023-01-01',
serviceRefs: {ref1: 'service1', ref2: 'service2'},
},
};
const result: any = selectVc(mockState);
expect(result.verificationStatus).toBe('verified');
expect(result.generatedOn).toBe('2023-01-01');
expect(result.serviceRefs).toBeUndefined();
});
});
});

View File

@@ -0,0 +1,288 @@
import {VcMetaEvents} from './VCMetaEvents';
import {VCMetadata} from '../../../shared/VCMetadata';
import {VC} from './vc';
describe('VcMetaEvents', () => {
describe('VIEW_VC', () => {
it('should create event with vc', () => {
const vc = {id: 'vc-123'} as unknown as VC;
const result = VcMetaEvents.VIEW_VC(vc);
expect(result).toEqual({vc});
});
});
describe('GET_VC_ITEM', () => {
it('should create event with vcMetadata', () => {
const vcMetadata = new VCMetadata();
const result = VcMetaEvents.GET_VC_ITEM(vcMetadata);
expect(result).toEqual({vcMetadata});
});
});
describe('STORE_RESPONSE', () => {
it('should create event with response', () => {
const response = {data: 'test'};
const result = VcMetaEvents.STORE_RESPONSE(response);
expect(result).toEqual({response: {data: 'test'}});
});
it('should handle undefined response', () => {
const result = VcMetaEvents.STORE_RESPONSE(undefined);
expect(result).toEqual({response: undefined});
});
});
describe('STORE_ERROR', () => {
it('should create event with error', () => {
const error = new Error('Test error');
const result = VcMetaEvents.STORE_ERROR(error);
expect(result).toEqual({error});
});
});
describe('VC_ADDED', () => {
it('should create event with vcMetadata', () => {
const vcMetadata = new VCMetadata();
const result = VcMetaEvents.VC_ADDED(vcMetadata);
expect(result).toEqual({vcMetadata});
});
});
describe('REMOVE_VC_FROM_CONTEXT', () => {
it('should create event with vcMetadata', () => {
const vcMetadata = new VCMetadata();
const result = VcMetaEvents.REMOVE_VC_FROM_CONTEXT(vcMetadata);
expect(result).toEqual({vcMetadata});
});
});
describe('VC_METADATA_UPDATED', () => {
it('should create event with vcMetadata', () => {
const vcMetadata = new VCMetadata();
const result = VcMetaEvents.VC_METADATA_UPDATED(vcMetadata);
expect(result).toEqual({vcMetadata});
});
});
describe('VC_DOWNLOADED', () => {
it('should create event with vc and vcMetadata', () => {
const vc = {id: 'vc-123'} as unknown as VC;
const vcMetadata = new VCMetadata();
const result = VcMetaEvents.VC_DOWNLOADED(vc, vcMetadata);
expect(result.vc).toBe(vc);
expect(result.vcMetadata).toBe(vcMetadata);
});
it('should handle undefined vcMetadata', () => {
const vc = {id: 'vc-123'} as unknown as VC;
const result = VcMetaEvents.VC_DOWNLOADED(vc);
expect(result.vc).toBe(vc);
expect(result.vcMetadata).toBeUndefined();
});
});
describe('REFRESH_MY_VCS', () => {
it('should create empty event', () => {
const result = VcMetaEvents.REFRESH_MY_VCS();
expect(result).toEqual({});
});
});
describe('REFRESH_MY_VCS_TWO', () => {
it('should create event with vc', () => {
const vc = {id: 'vc-123'} as unknown as VC;
const result = VcMetaEvents.REFRESH_MY_VCS_TWO(vc);
expect(result).toEqual({vc});
});
});
describe('REFRESH_RECEIVED_VCS', () => {
it('should create empty event', () => {
const result = VcMetaEvents.REFRESH_RECEIVED_VCS();
expect(result).toEqual({});
});
});
describe('WALLET_BINDING_SUCCESS', () => {
it('should create event with vcKey and vc', () => {
const vcKey = 'key-123';
const vc = {id: 'vc-123'} as unknown as VC;
const result = VcMetaEvents.WALLET_BINDING_SUCCESS(vcKey, vc);
expect(result).toEqual({vcKey: 'key-123', vc});
});
});
describe('RESET_WALLET_BINDING_SUCCESS', () => {
it('should create empty event', () => {
const result = VcMetaEvents.RESET_WALLET_BINDING_SUCCESS();
expect(result).toEqual({});
});
});
describe('ADD_VC_TO_IN_PROGRESS_DOWNLOADS', () => {
it('should create event with requestId', () => {
const result = VcMetaEvents.ADD_VC_TO_IN_PROGRESS_DOWNLOADS('req-123');
expect(result).toEqual({requestId: 'req-123'});
});
});
describe('REMOVE_VC_FROM_IN_PROGRESS_DOWNLOADS', () => {
it('should create event with vcMetadata', () => {
const vcMetadata = new VCMetadata();
const result =
VcMetaEvents.REMOVE_VC_FROM_IN_PROGRESS_DOWNLOADS(vcMetadata);
expect(result).toEqual({vcMetadata});
});
});
describe('RESET_IN_PROGRESS_VCS_DOWNLOADED', () => {
it('should create empty event', () => {
const result = VcMetaEvents.RESET_IN_PROGRESS_VCS_DOWNLOADED();
expect(result).toEqual({});
});
});
describe('REMOVE_TAMPERED_VCS', () => {
it('should create empty event', () => {
const result = VcMetaEvents.REMOVE_TAMPERED_VCS();
expect(result).toEqual({});
});
});
describe('DOWNLOAD_LIMIT_EXPIRED', () => {
it('should create event with vcMetadata', () => {
const vcMetadata = new VCMetadata();
const result = VcMetaEvents.DOWNLOAD_LIMIT_EXPIRED(vcMetadata);
expect(result).toEqual({vcMetadata});
});
});
describe('DELETE_VC', () => {
it('should create empty event', () => {
const result = VcMetaEvents.DELETE_VC();
expect(result).toEqual({});
});
});
describe('VERIFY_VC_FAILED', () => {
it('should create event with errorMessage and vcMetadata', () => {
const vcMetadata = new VCMetadata();
const result = VcMetaEvents.VERIFY_VC_FAILED(
'Verification failed',
vcMetadata,
);
expect(result.errorMessage).toBe('Verification failed');
expect(result.vcMetadata).toBe(vcMetadata);
});
it('should handle undefined vcMetadata', () => {
const result = VcMetaEvents.VERIFY_VC_FAILED('Error occurred');
expect(result.errorMessage).toBe('Error occurred');
expect(result.vcMetadata).toBeUndefined();
});
});
describe('RESET_VERIFY_ERROR', () => {
it('should create empty event', () => {
const result = VcMetaEvents.RESET_VERIFY_ERROR();
expect(result).toEqual({});
});
});
describe('REFRESH_VCS_METADATA', () => {
it('should create empty event', () => {
const result = VcMetaEvents.REFRESH_VCS_METADATA();
expect(result).toEqual({});
});
});
describe('SHOW_TAMPERED_POPUP', () => {
it('should create empty event', () => {
const result = VcMetaEvents.SHOW_TAMPERED_POPUP();
expect(result).toEqual({});
});
});
describe('SET_VERIFICATION_STATUS', () => {
it('should create event with verificationStatus', () => {
const status = {verified: true};
const result = VcMetaEvents.SET_VERIFICATION_STATUS(status);
expect(result).toEqual({verificationStatus: status});
});
});
describe('RESET_VERIFICATION_STATUS', () => {
it('should create event with verificationStatus', () => {
const status = {message: 'Reset'} as any;
const result = VcMetaEvents.RESET_VERIFICATION_STATUS(status);
expect(result).toEqual({verificationStatus: status});
});
it('should handle null verificationStatus', () => {
const result = VcMetaEvents.RESET_VERIFICATION_STATUS(null);
expect(result).toEqual({verificationStatus: null});
});
});
describe('VC_DOWNLOADING_FAILED', () => {
it('should create empty event', () => {
const result = VcMetaEvents.VC_DOWNLOADING_FAILED();
expect(result).toEqual({});
});
});
describe('RESET_DOWNLOADING_FAILED', () => {
it('should create empty event', () => {
const result = VcMetaEvents.RESET_DOWNLOADING_FAILED();
expect(result).toEqual({});
});
});
describe('RESET_DOWNLOADING_SUCCESS', () => {
it('should create empty event', () => {
const result = VcMetaEvents.RESET_DOWNLOADING_SUCCESS();
expect(result).toEqual({});
});
});
describe('VcMetaEvents object structure', () => {
it('should have all expected event creators', () => {
expect(VcMetaEvents.VIEW_VC).toBeDefined();
expect(VcMetaEvents.GET_VC_ITEM).toBeDefined();
expect(VcMetaEvents.STORE_RESPONSE).toBeDefined();
expect(VcMetaEvents.STORE_ERROR).toBeDefined();
expect(VcMetaEvents.VC_ADDED).toBeDefined();
expect(VcMetaEvents.REMOVE_VC_FROM_CONTEXT).toBeDefined();
expect(VcMetaEvents.VC_METADATA_UPDATED).toBeDefined();
expect(VcMetaEvents.VC_DOWNLOADED).toBeDefined();
expect(VcMetaEvents.REFRESH_MY_VCS).toBeDefined();
expect(VcMetaEvents.REFRESH_MY_VCS_TWO).toBeDefined();
expect(VcMetaEvents.REFRESH_RECEIVED_VCS).toBeDefined();
expect(VcMetaEvents.WALLET_BINDING_SUCCESS).toBeDefined();
expect(VcMetaEvents.RESET_WALLET_BINDING_SUCCESS).toBeDefined();
expect(VcMetaEvents.ADD_VC_TO_IN_PROGRESS_DOWNLOADS).toBeDefined();
expect(VcMetaEvents.REMOVE_VC_FROM_IN_PROGRESS_DOWNLOADS).toBeDefined();
expect(VcMetaEvents.RESET_IN_PROGRESS_VCS_DOWNLOADED).toBeDefined();
expect(VcMetaEvents.REMOVE_TAMPERED_VCS).toBeDefined();
expect(VcMetaEvents.DOWNLOAD_LIMIT_EXPIRED).toBeDefined();
expect(VcMetaEvents.DELETE_VC).toBeDefined();
expect(VcMetaEvents.VERIFY_VC_FAILED).toBeDefined();
expect(VcMetaEvents.RESET_VERIFY_ERROR).toBeDefined();
expect(VcMetaEvents.REFRESH_VCS_METADATA).toBeDefined();
expect(VcMetaEvents.SHOW_TAMPERED_POPUP).toBeDefined();
expect(VcMetaEvents.SET_VERIFICATION_STATUS).toBeDefined();
expect(VcMetaEvents.RESET_VERIFICATION_STATUS).toBeDefined();
expect(VcMetaEvents.VC_DOWNLOADING_FAILED).toBeDefined();
expect(VcMetaEvents.RESET_DOWNLOADING_FAILED).toBeDefined();
expect(VcMetaEvents.RESET_DOWNLOADING_SUCCESS).toBeDefined();
});
it('should have all event creators be functions', () => {
expect(typeof VcMetaEvents.VIEW_VC).toBe('function');
expect(typeof VcMetaEvents.GET_VC_ITEM).toBe('function');
expect(typeof VcMetaEvents.STORE_RESPONSE).toBe('function');
expect(typeof VcMetaEvents.STORE_ERROR).toBe('function');
expect(typeof VcMetaEvents.VC_ADDED).toBe('function');
});
});
});

View File

@@ -0,0 +1,219 @@
import {VCMetamodel} from './VCMetaModel';
describe('VCMetaModel', () => {
describe('Model structure', () => {
it('should be defined', () => {
expect(VCMetamodel).toBeDefined();
});
it('should have initialContext', () => {
expect(VCMetamodel.initialContext).toBeDefined();
});
it('should have events', () => {
expect(VCMetamodel.events).toBeDefined();
});
});
describe('Initial Context', () => {
const initialContext = VCMetamodel.initialContext;
it('should have serviceRefs as empty object', () => {
expect(initialContext.serviceRefs).toEqual({});
expect(typeof initialContext.serviceRefs).toBe('object');
});
it('should have myVcsMetadata as empty array', () => {
expect(initialContext.myVcsMetadata).toEqual([]);
expect(Array.isArray(initialContext.myVcsMetadata)).toBe(true);
expect(initialContext.myVcsMetadata).toHaveLength(0);
});
it('should have receivedVcsMetadata as empty array', () => {
expect(initialContext.receivedVcsMetadata).toEqual([]);
expect(Array.isArray(initialContext.receivedVcsMetadata)).toBe(true);
expect(initialContext.receivedVcsMetadata).toHaveLength(0);
});
it('should have myVcs as empty object', () => {
expect(initialContext.myVcs).toEqual({});
expect(typeof initialContext.myVcs).toBe('object');
expect(Object.keys(initialContext.myVcs)).toHaveLength(0);
});
it('should have receivedVcs as empty object', () => {
expect(initialContext.receivedVcs).toEqual({});
expect(typeof initialContext.receivedVcs).toBe('object');
expect(Object.keys(initialContext.receivedVcs)).toHaveLength(0);
});
it('should have inProgressVcDownloads as empty Set', () => {
expect(initialContext.inProgressVcDownloads).toBeInstanceOf(Set);
expect(initialContext.inProgressVcDownloads.size).toBe(0);
});
it('should have areAllVcsDownloaded as false', () => {
expect(initialContext.areAllVcsDownloaded).toBe(false);
expect(typeof initialContext.areAllVcsDownloaded).toBe('boolean');
});
it('should have walletBindingSuccess as false', () => {
expect(initialContext.walletBindingSuccess).toBe(false);
expect(typeof initialContext.walletBindingSuccess).toBe('boolean');
});
it('should have tamperedVcs as empty array', () => {
expect(initialContext.tamperedVcs).toEqual([]);
expect(Array.isArray(initialContext.tamperedVcs)).toBe(true);
expect(initialContext.tamperedVcs).toHaveLength(0);
});
it('should have downloadingFailedVcs as empty array', () => {
expect(initialContext.downloadingFailedVcs).toEqual([]);
expect(Array.isArray(initialContext.downloadingFailedVcs)).toBe(true);
expect(initialContext.downloadingFailedVcs).toHaveLength(0);
});
it('should have verificationErrorMessage as empty string', () => {
expect(initialContext.verificationErrorMessage).toBe('');
expect(typeof initialContext.verificationErrorMessage).toBe('string');
});
it('should have verificationStatus as null', () => {
expect(initialContext.verificationStatus).toBeNull();
});
it('should have DownloadingCredentialsFailed as false', () => {
expect(initialContext.DownloadingCredentialsFailed).toBe(false);
expect(typeof initialContext.DownloadingCredentialsFailed).toBe(
'boolean',
);
});
it('should have DownloadingCredentialsSuccess as false', () => {
expect(initialContext.DownloadingCredentialsSuccess).toBe(false);
expect(typeof initialContext.DownloadingCredentialsSuccess).toBe(
'boolean',
);
});
it('should have all required properties', () => {
expect(initialContext).toHaveProperty('serviceRefs');
expect(initialContext).toHaveProperty('myVcsMetadata');
expect(initialContext).toHaveProperty('receivedVcsMetadata');
expect(initialContext).toHaveProperty('myVcs');
expect(initialContext).toHaveProperty('receivedVcs');
expect(initialContext).toHaveProperty('inProgressVcDownloads');
expect(initialContext).toHaveProperty('areAllVcsDownloaded');
expect(initialContext).toHaveProperty('walletBindingSuccess');
expect(initialContext).toHaveProperty('tamperedVcs');
expect(initialContext).toHaveProperty('downloadingFailedVcs');
expect(initialContext).toHaveProperty('verificationErrorMessage');
expect(initialContext).toHaveProperty('verificationStatus');
expect(initialContext).toHaveProperty('DownloadingCredentialsFailed');
expect(initialContext).toHaveProperty('DownloadingCredentialsSuccess');
});
it('should have exactly 16 properties in initial context', () => {
const propertyCount = Object.keys(initialContext).length;
expect(propertyCount).toBe(16);
});
});
describe('Model events', () => {
it('should have events object defined', () => {
expect(VCMetamodel.events).toBeDefined();
expect(typeof VCMetamodel.events).toBe('object');
});
it('should have non-empty events', () => {
const eventKeys = Object.keys(VCMetamodel.events);
expect(eventKeys.length).toBeGreaterThan(0);
});
});
describe('Type validation', () => {
const context = VCMetamodel.initialContext;
it('myVcsMetadata should accept VCMetadata array', () => {
expect(() => {
const metadata: typeof context.myVcsMetadata = [];
expect(Array.isArray(metadata)).toBe(true);
}).not.toThrow();
});
it('myVcs should accept Record<string, VC>', () => {
expect(() => {
const vcs: typeof context.myVcs = {};
expect(typeof vcs).toBe('object');
}).not.toThrow();
});
it('inProgressVcDownloads should be a Set', () => {
expect(context.inProgressVcDownloads).toBeInstanceOf(Set);
expect(context.inProgressVcDownloads.constructor.name).toBe('Set');
});
});
describe('Boolean flags', () => {
const context = VCMetamodel.initialContext;
it('all boolean flags should be false initially', () => {
const booleanFlags = [
context.areAllVcsDownloaded,
context.walletBindingSuccess,
context.DownloadingCredentialsFailed,
context.DownloadingCredentialsSuccess,
];
booleanFlags.forEach(flag => {
expect(flag).toBe(false);
});
});
});
describe('Array properties', () => {
const context = VCMetamodel.initialContext;
it('all array properties should be empty initially', () => {
const arrays = [
context.myVcsMetadata,
context.receivedVcsMetadata,
context.tamperedVcs,
context.downloadingFailedVcs,
];
arrays.forEach(arr => {
expect(Array.isArray(arr)).toBe(true);
expect(arr).toHaveLength(0);
});
});
});
describe('Object properties', () => {
const context = VCMetamodel.initialContext;
it('all object properties should be empty initially', () => {
const objects = [context.serviceRefs, context.myVcs, context.receivedVcs];
objects.forEach(obj => {
expect(typeof obj).toBe('object');
expect(Object.keys(obj)).toHaveLength(0);
});
});
});
describe('Null/undefined properties', () => {
const context = VCMetamodel.initialContext;
it('verificationStatus should be null', () => {
expect(context.verificationStatus).toBeNull();
expect(context.verificationStatus).not.toBeUndefined();
});
it('verificationErrorMessage should be empty string, not null', () => {
expect(context.verificationErrorMessage).not.toBeNull();
expect(context.verificationErrorMessage).toBe('');
});
});
});

View File

@@ -0,0 +1,510 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
selectVerificationStatus,
selectMyVcsMetadata,
selectShareableVcsMetadata,
selectShareableVcs,
selectReceivedVcsMetadata,
selectIsRefreshingMyVcs,
selectIsRefreshingReceivedVcs,
selectAreAllVcsDownloaded,
selectBindedVcsMetadata,
selectInProgressVcDownloads,
selectWalletBindingSuccess,
selectIsTampered,
selectDownloadingFailedVcs,
selectMyVcs,
selectVerificationErrorMessage,
selectIsDownloadingFailed,
selectIsDownloadingSuccess,
} from './VCMetaSelectors';
import {VCMetadata} from '../../../shared/VCMetadata';
describe('VCMetaSelectors', () => {
const mockVcMetadata1 = new VCMetadata({
id: 'vc1',
idType: 'NationalID',
issuer: 'Test Issuer 1',
});
const mockVcMetadata2 = new VCMetadata({
id: 'vc2',
idType: 'Passport',
issuer: 'Test Issuer 2',
});
const mockVcMetadata3 = new VCMetadata({
id: 'vc3',
idType: 'DriversLicense',
issuer: 'Test Issuer 3',
});
const mockVc1 = {
verifiableCredential: {credential: {id: 'cred1'}},
walletBindingResponse: null,
};
const mockVc2 = {
verifiableCredential: {credential: {id: 'cred2'}},
walletBindingResponse: {walletBindingId: 'binding123'},
};
const mockVc3 = {
verifiableCredential: null,
walletBindingResponse: null,
};
const mockState: any = {
context: {
verificationStatus: 'verified',
myVcsMetadata: [mockVcMetadata1, mockVcMetadata2, mockVcMetadata3],
receivedVcsMetadata: [mockVcMetadata1],
myVcs: {
[mockVcMetadata1.getVcKey()]: mockVc1,
[mockVcMetadata2.getVcKey()]: mockVc2,
[mockVcMetadata3.getVcKey()]: mockVc3,
},
areAllVcsDownloaded: true,
inProgressVcDownloads: [],
walletBindingSuccess: false,
downloadingFailedVcs: [],
},
matches: jest.fn((stateName: string) => stateName === 'ready.myVcs'),
};
describe('selectVerificationStatus', () => {
it('should return verification status from context', () => {
const result = selectVerificationStatus(mockState);
expect(result).toBe('verified');
});
it('should handle different status values', () => {
const statuses = ['verified', 'pending', 'failed', 'invalid'];
statuses.forEach(status => {
const state: any = {
...mockState,
context: {...mockState.context, verificationStatus: status},
};
expect(selectVerificationStatus(state)).toBe(status);
});
});
});
describe('selectMyVcsMetadata', () => {
it('should return all VCs metadata from context', () => {
const result = selectMyVcsMetadata(mockState);
expect(result).toHaveLength(3);
expect(result).toEqual([
mockVcMetadata1,
mockVcMetadata2,
mockVcMetadata3,
]);
});
it('should return empty array when no VCs', () => {
const state: any = {
...mockState,
context: {...mockState.context, myVcsMetadata: []},
};
const result = selectMyVcsMetadata(state);
expect(result).toEqual([]);
});
});
describe('selectShareableVcsMetadata', () => {
it('should filter VCs that have verifiableCredential', () => {
const result = selectShareableVcsMetadata(mockState);
expect(result).toHaveLength(2);
expect(result).toContain(mockVcMetadata1);
expect(result).toContain(mockVcMetadata2);
expect(result).not.toContain(mockVcMetadata3);
});
it('should return empty array when no shareable VCs', () => {
const state: any = {
...mockState,
context: {
...mockState.context,
myVcs: {
[mockVcMetadata1.getVcKey()]: {verifiableCredential: null},
[mockVcMetadata2.getVcKey()]: {verifiableCredential: null},
},
},
};
const result = selectShareableVcsMetadata(state);
expect(result).toEqual([]);
});
});
describe('selectShareableVcs', () => {
it('should filter VCs that have verifiableCredential', () => {
const result = selectShareableVcs(mockState);
expect(result).toHaveLength(2);
expect(result).toContainEqual(mockVc1);
expect(result).toContainEqual(mockVc2);
expect(result).not.toContainEqual(mockVc3);
});
it('should return empty array when no VCs have verifiableCredential', () => {
const state: any = {
...mockState,
context: {
...mockState.context,
myVcs: {
vc1: {verifiableCredential: null},
vc2: {verifiableCredential: null},
},
},
};
const result = selectShareableVcs(state);
expect(result).toEqual([]);
});
});
describe('selectReceivedVcsMetadata', () => {
it('should return received VCs metadata', () => {
const result = selectReceivedVcsMetadata(mockState);
expect(result).toHaveLength(1);
expect(result).toContain(mockVcMetadata1);
});
it('should return empty array when no received VCs', () => {
const state: any = {
...mockState,
context: {...mockState.context, receivedVcsMetadata: []},
};
const result = selectReceivedVcsMetadata(state);
expect(result).toEqual([]);
});
});
describe('selectIsRefreshingMyVcs', () => {
it('should return true when in ready.myVcs state', () => {
const result = selectIsRefreshingMyVcs(mockState);
expect(result).toBe(true);
});
it('should call matches with ready.myVcs', () => {
selectIsRefreshingMyVcs(mockState);
expect(mockState.matches).toHaveBeenCalledWith('ready.myVcs');
});
it('should return false when not in ready.myVcs state', () => {
const state: any = {
...mockState,
matches: jest.fn(() => false),
};
const result = selectIsRefreshingMyVcs(state);
expect(result).toBe(false);
});
});
describe('selectIsRefreshingReceivedVcs', () => {
it('should return true when in ready.receivedVcs state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'ready.receivedVcs'),
};
const result = selectIsRefreshingReceivedVcs(state);
expect(result).toBe(true);
});
it('should call matches with ready.receivedVcs', () => {
const state: any = {
...mockState,
matches: jest.fn(() => false),
};
selectIsRefreshingReceivedVcs(state);
expect(state.matches).toHaveBeenCalledWith('ready.receivedVcs');
});
});
describe('selectAreAllVcsDownloaded', () => {
it('should return true when all VCs are downloaded', () => {
const result = selectAreAllVcsDownloaded(mockState);
expect(result).toBe(true);
});
it('should return false when not all VCs are downloaded', () => {
const state: any = {
...mockState,
context: {...mockState.context, areAllVcsDownloaded: false},
};
const result = selectAreAllVcsDownloaded(state);
expect(result).toBe(false);
});
});
describe('selectBindedVcsMetadata', () => {
it('should return VCs with wallet binding', () => {
const result = selectBindedVcsMetadata(mockState);
expect(result).toHaveLength(1);
expect(result).toContain(mockVcMetadata2);
expect(result).not.toContain(mockVcMetadata1);
expect(result).not.toContain(mockVcMetadata3);
});
it('should filter out VCs with null walletBindingResponse', () => {
const state: any = {
...mockState,
context: {
...mockState.context,
myVcs: {
[mockVcMetadata1.getVcKey()]: {walletBindingResponse: null},
[mockVcMetadata2.getVcKey()]: {walletBindingResponse: {}},
},
},
};
const result = selectBindedVcsMetadata(state);
expect(result).toEqual([]);
});
it('should filter out VCs with empty walletBindingId', () => {
const state: any = {
...mockState,
context: {
...mockState.context,
myVcs: {
[mockVcMetadata1.getVcKey()]: {
walletBindingResponse: {walletBindingId: ''},
},
[mockVcMetadata2.getVcKey()]: {
walletBindingResponse: {walletBindingId: null},
},
},
},
};
const result = selectBindedVcsMetadata(state);
expect(result).toEqual([]);
});
it('should return empty array when no binded VCs', () => {
const state: any = {
...mockState,
context: {
...mockState.context,
myVcs: {
[mockVcMetadata1.getVcKey()]: {walletBindingResponse: null},
},
},
};
const result = selectBindedVcsMetadata(state);
expect(result).toEqual([]);
});
});
describe('selectInProgressVcDownloads', () => {
it('should return in-progress VC downloads', () => {
const downloads = ['vc1', 'vc2'];
const state: any = {
...mockState,
context: {...mockState.context, inProgressVcDownloads: downloads},
};
const result = selectInProgressVcDownloads(state);
expect(result).toEqual(downloads);
});
it('should return empty array when no downloads in progress', () => {
const result = selectInProgressVcDownloads(mockState);
expect(result).toEqual([]);
});
});
describe('selectWalletBindingSuccess', () => {
it('should return wallet binding success status', () => {
const result = selectWalletBindingSuccess(mockState);
expect(result).toBe(false);
});
it('should return true when wallet binding is successful', () => {
const state: any = {
...mockState,
context: {...mockState.context, walletBindingSuccess: true},
};
const result = selectWalletBindingSuccess(state);
expect(result).toBe(true);
});
});
describe('selectIsTampered', () => {
it('should return true when in ready.tamperedVCs state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'ready.tamperedVCs'),
};
const result = selectIsTampered(state);
expect(result).toBe(true);
});
it('should call matches with ready.tamperedVCs', () => {
const state: any = {
...mockState,
matches: jest.fn(() => false),
};
selectIsTampered(state);
expect(state.matches).toHaveBeenCalledWith('ready.tamperedVCs');
});
it('should return false when not in tampered state', () => {
const state: any = {
...mockState,
matches: jest.fn(() => false),
};
const result = selectIsTampered(state);
expect(result).toBe(false);
});
});
describe('selectDownloadingFailedVcs', () => {
it('should return downloading failed VCs', () => {
const failedVcs = ['vc1', 'vc2'];
const state: any = {
...mockState,
context: {...mockState.context, downloadingFailedVcs: failedVcs},
};
const result = selectDownloadingFailedVcs(state);
expect(result).toEqual(failedVcs);
});
it('should return empty array when no failed VCs', () => {
const result = selectDownloadingFailedVcs(mockState);
expect(result).toEqual([]);
});
});
describe('selectMyVcs', () => {
it('should return all my VCs object', () => {
const result = selectMyVcs(mockState);
expect(result).toEqual(mockState.context.myVcs);
});
it('should return object with VC keys', () => {
const result = selectMyVcs(mockState);
expect(result).toHaveProperty(mockVcMetadata1.getVcKey());
expect(result).toHaveProperty(mockVcMetadata2.getVcKey());
});
it('should return empty object when no VCs', () => {
const state: any = {
...mockState,
context: {...mockState.context, myVcs: {}},
};
const result = selectMyVcs(state);
expect(result).toEqual({});
});
});
describe('Edge cases and boundary conditions', () => {
it('should handle undefined values in walletBindingResponse', () => {
const state: any = {
...mockState,
context: {
...mockState.context,
myVcs: {
[mockVcMetadata1.getVcKey()]: {
walletBindingResponse: undefined,
},
},
},
};
const result = selectBindedVcsMetadata(state);
expect(result).toEqual([]);
});
it('should handle empty string in walletBindingId', () => {
const state: any = {
...mockState,
context: {
...mockState.context,
myVcs: {
[mockVcMetadata1.getVcKey()]: {
walletBindingResponse: {walletBindingId: ''},
},
},
},
};
const result = selectBindedVcsMetadata(state);
expect(result).toEqual([]);
});
it('should handle null verifiableCredential in filtering', () => {
const state: any = {
...mockState,
context: {
...mockState.context,
myVcs: {
[mockVcMetadata1.getVcKey()]: {verifiableCredential: null},
},
},
};
const result = selectShareableVcsMetadata(state);
expect(result).toEqual([]);
});
});
describe('selectVerificationErrorMessage', () => {
it('should return verification error message from context', () => {
const state: any = {
context: {
verificationErrorMessage: 'Invalid signature',
},
};
const result = selectVerificationErrorMessage(state);
expect(result).toBe('Invalid signature');
});
it('should return empty string when no error', () => {
const state: any = {
context: {
verificationErrorMessage: '',
},
};
const result = selectVerificationErrorMessage(state);
expect(result).toBe('');
});
});
describe('selectIsDownloadingFailed', () => {
it('should return DownloadingCredentialsFailed status', () => {
const state: any = {
context: {
DownloadingCredentialsFailed: true,
},
};
const result = selectIsDownloadingFailed(state);
expect(result).toBe(true);
});
it('should return false when downloading not failed', () => {
const state: any = {
context: {
DownloadingCredentialsFailed: false,
},
};
const result = selectIsDownloadingFailed(state);
expect(result).toBe(false);
});
});
describe('selectIsDownloadingSuccess', () => {
it('should return DownloadingCredentialsSuccess status', () => {
const state: any = {
context: {
DownloadingCredentialsSuccess: true,
},
};
const result = selectIsDownloadingSuccess(state);
expect(result).toBe(true);
});
it('should return false when downloading not successful', () => {
const state: any = {
context: {
DownloadingCredentialsSuccess: false,
},
};
const result = selectIsDownloadingSuccess(state);
expect(result).toBe(false);
});
});
});

View File

@@ -0,0 +1,151 @@
import {backupModel} from './backupModel';
describe('backupModel', () => {
describe('Model structure', () => {
it('should be defined', () => {
expect(backupModel).toBeDefined();
});
it('should have initialContext', () => {
expect(backupModel.initialContext).toBeDefined();
});
it('should have events', () => {
expect(backupModel.events).toBeDefined();
});
});
describe('Initial Context', () => {
const initialContext = backupModel.initialContext;
it('should have serviceRefs as empty object', () => {
expect(initialContext.serviceRefs).toEqual({});
expect(typeof initialContext.serviceRefs).toBe('object');
});
it('should have dataFromStorage as empty object', () => {
expect(initialContext.dataFromStorage).toEqual({});
expect(typeof initialContext.dataFromStorage).toBe('object');
});
it('should have fileName as empty string', () => {
expect(initialContext.fileName).toBe('');
expect(typeof initialContext.fileName).toBe('string');
});
it('should have lastBackupDetails as null', () => {
expect(initialContext.lastBackupDetails).toBeNull();
});
it('should have errorReason as empty string', () => {
expect(initialContext.errorReason).toBe('');
expect(typeof initialContext.errorReason).toBe('string');
});
it('should have isAutoBackUp as true', () => {
expect(initialContext.isAutoBackUp).toBe(true);
expect(typeof initialContext.isAutoBackUp).toBe('boolean');
});
it('should have isLoadingBackupDetails as true', () => {
expect(initialContext.isLoadingBackupDetails).toBe(true);
expect(typeof initialContext.isLoadingBackupDetails).toBe('boolean');
});
it('should have showBackupInProgress as false', () => {
expect(initialContext.showBackupInProgress).toBe(false);
expect(typeof initialContext.showBackupInProgress).toBe('boolean');
});
it('should have all 8 required properties', () => {
const properties = Object.keys(initialContext);
expect(properties).toHaveLength(8);
});
});
describe('String properties', () => {
const context = backupModel.initialContext;
it('all empty string properties should be empty', () => {
const emptyStrings = [context.fileName, context.errorReason];
emptyStrings.forEach(str => {
expect(str).toBe('');
expect(typeof str).toBe('string');
});
});
});
describe('Object properties', () => {
const context = backupModel.initialContext;
it('all empty object properties should be empty objects', () => {
const emptyObjects = [context.serviceRefs, context.dataFromStorage];
emptyObjects.forEach(obj => {
expect(typeof obj).toBe('object');
expect(Object.keys(obj)).toHaveLength(0);
});
});
});
describe('Boolean properties', () => {
const context = backupModel.initialContext;
it('isAutoBackUp should be true', () => {
expect(context.isAutoBackUp).toBe(true);
expect(typeof context.isAutoBackUp).toBe('boolean');
});
it('isLoadingBackupDetails should be true', () => {
expect(context.isLoadingBackupDetails).toBe(true);
expect(typeof context.isLoadingBackupDetails).toBe('boolean');
});
it('showBackupInProgress should be false', () => {
expect(context.showBackupInProgress).toBe(false);
expect(typeof context.showBackupInProgress).toBe('boolean');
});
it('should have correct initial values for boolean properties', () => {
expect(context.isAutoBackUp).toBe(true);
expect(context.isLoadingBackupDetails).toBe(true);
expect(context.showBackupInProgress).toBe(false);
});
});
describe('Null properties', () => {
const context = backupModel.initialContext;
it('lastBackupDetails should be null', () => {
expect(context.lastBackupDetails).toBeNull();
});
});
describe('Model events', () => {
it('should have events object', () => {
expect(backupModel.events).toBeDefined();
expect(typeof backupModel.events).toBe('object');
});
it('should have event creators', () => {
const eventKeys = Object.keys(backupModel.events);
expect(eventKeys.length).toBeGreaterThan(0);
});
});
describe('Property types validation', () => {
const context = backupModel.initialContext;
it('should have correct types for all properties', () => {
expect(typeof context.serviceRefs).toBe('object');
expect(typeof context.dataFromStorage).toBe('object');
expect(typeof context.fileName).toBe('string');
expect(context.lastBackupDetails).toBeNull();
expect(typeof context.errorReason).toBe('string');
expect(typeof context.isAutoBackUp).toBe('boolean');
expect(typeof context.isLoadingBackupDetails).toBe('boolean');
expect(typeof context.showBackupInProgress).toBe('boolean');
});
});
});

View File

@@ -0,0 +1,214 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
selectIsBackupInprogress,
selectIsLoadingBackupDetails,
selectIsBackingUpSuccess,
selectIsBackingUpFailure,
selectIsNetworkError,
lastBackupDetails,
selectBackupErrorReason,
selectShowBackupInProgress,
} from './backupSelector';
describe('backupSelector', () => {
describe('selectIsBackupInprogress', () => {
it('should return true when in checkDataAvailabilityForBackup state', () => {
const mockState: any = {
matches: jest.fn(
(state: string) =>
state === 'backingUp.checkDataAvailabilityForBackup',
),
};
expect(selectIsBackupInprogress(mockState)).toBe(true);
});
it('should return true when in checkStorageAvailability state', () => {
const mockState: any = {
matches: jest.fn(
(state: string) => state === 'backingUp.checkStorageAvailability',
),
};
expect(selectIsBackupInprogress(mockState)).toBe(true);
});
it('should return true when in fetchDataFromDB state', () => {
const mockState: any = {
matches: jest.fn(
(state: string) => state === 'backingUp.fetchDataFromDB',
),
};
expect(selectIsBackupInprogress(mockState)).toBe(true);
});
it('should return true when in writeDataToFile state', () => {
const mockState: any = {
matches: jest.fn(
(state: string) => state === 'backingUp.writeDataToFile',
),
};
expect(selectIsBackupInprogress(mockState)).toBe(true);
});
it('should return true when in zipBackupFile state', () => {
const mockState: any = {
matches: jest.fn(
(state: string) => state === 'backingUp.zipBackupFile',
),
};
expect(selectIsBackupInprogress(mockState)).toBe(true);
});
it('should return true when in uploadBackupFile state', () => {
const mockState: any = {
matches: jest.fn(
(state: string) => state === 'backingUp.uploadBackupFile',
),
};
expect(selectIsBackupInprogress(mockState)).toBe(true);
});
it('should return false when not in any backup progress state', () => {
const mockState: any = {
matches: jest.fn(() => false),
};
expect(selectIsBackupInprogress(mockState)).toBe(false);
});
});
describe('selectIsLoadingBackupDetails', () => {
it('should return isLoadingBackupDetails from context', () => {
const mockState: any = {
context: {
isLoadingBackupDetails: true,
},
};
expect(selectIsLoadingBackupDetails(mockState)).toBe(true);
});
it('should return false when isLoadingBackupDetails is false', () => {
const mockState: any = {
context: {
isLoadingBackupDetails: false,
},
};
expect(selectIsLoadingBackupDetails(mockState)).toBe(false);
});
});
describe('selectIsBackingUpSuccess', () => {
it('should return true when in backingUp.success state', () => {
const mockState: any = {
matches: jest.fn((state: string) => state === 'backingUp.success'),
};
expect(selectIsBackingUpSuccess(mockState)).toBe(true);
});
it('should return false when not in success state', () => {
const mockState: any = {
matches: jest.fn(() => false),
};
expect(selectIsBackingUpSuccess(mockState)).toBe(false);
});
});
describe('selectIsBackingUpFailure', () => {
it('should return true when in backingUp.failure state', () => {
const mockState: any = {
matches: jest.fn((state: string) => state === 'backingUp.failure'),
};
expect(selectIsBackingUpFailure(mockState)).toBe(true);
});
it('should return false when not in failure state', () => {
const mockState: any = {
matches: jest.fn(() => false),
};
expect(selectIsBackingUpFailure(mockState)).toBe(false);
});
});
describe('selectIsNetworkError', () => {
it('should return true when in fetchLastBackupDetails.noInternet state', () => {
const mockState: any = {
matches: jest.fn(
(state: string) => state === 'fetchLastBackupDetails.noInternet',
),
};
expect(selectIsNetworkError(mockState)).toBe(true);
});
it('should return false when not in network error state', () => {
const mockState: any = {
matches: jest.fn(() => false),
};
expect(selectIsNetworkError(mockState)).toBe(false);
});
});
describe('lastBackupDetails', () => {
it('should return lastBackupDetails from context', () => {
const mockDetails = {
timestamp: '2024-01-01',
size: '10MB',
fileName: 'backup_123.zip',
};
const mockState: any = {
context: {
lastBackupDetails: mockDetails,
},
};
expect(lastBackupDetails(mockState)).toBe(mockDetails);
});
it('should return undefined when lastBackupDetails is not set', () => {
const mockState: any = {
context: {
lastBackupDetails: undefined,
},
};
expect(lastBackupDetails(mockState)).toBeUndefined();
});
});
describe('selectBackupErrorReason', () => {
it('should return errorReason from context', () => {
const mockState: any = {
context: {
errorReason: 'Insufficient storage space',
},
};
expect(selectBackupErrorReason(mockState)).toBe(
'Insufficient storage space',
);
});
it('should return null when no error', () => {
const mockState: any = {
context: {
errorReason: null,
},
};
expect(selectBackupErrorReason(mockState)).toBeNull();
});
});
describe('selectShowBackupInProgress', () => {
it('should return showBackupInProgress from context', () => {
const mockState: any = {
context: {
showBackupInProgress: true,
},
};
expect(selectShowBackupInProgress(mockState)).toBe(true);
});
it('should return false when showBackupInProgress is false', () => {
const mockState: any = {
context: {
showBackupInProgress: false,
},
};
expect(selectShowBackupInProgress(mockState)).toBe(false);
});
});
});

View File

@@ -0,0 +1,138 @@
import {backupAndRestoreSetupModel} from './backupAndRestoreSetupModel';
describe('backupAndRestoreSetupModel', () => {
describe('Model structure', () => {
it('should be defined', () => {
expect(backupAndRestoreSetupModel).toBeDefined();
});
it('should have initialContext', () => {
expect(backupAndRestoreSetupModel.initialContext).toBeDefined();
});
it('should have events', () => {
expect(backupAndRestoreSetupModel.events).toBeDefined();
});
});
describe('Initial Context', () => {
const initialContext = backupAndRestoreSetupModel.initialContext;
it('should have isLoading as false', () => {
expect(initialContext.isLoading).toBe(false);
expect(typeof initialContext.isLoading).toBe('boolean');
});
it('should have profileInfo as undefined', () => {
expect(initialContext.profileInfo).toBeUndefined();
});
it('should have errorMessage as empty string', () => {
expect(initialContext.errorMessage).toBe('');
expect(typeof initialContext.errorMessage).toBe('string');
});
it('should have serviceRefs as empty object', () => {
expect(initialContext.serviceRefs).toEqual({});
expect(typeof initialContext.serviceRefs).toBe('object');
});
it('should have shouldTriggerAutoBackup as false', () => {
expect(initialContext.shouldTriggerAutoBackup).toBe(false);
expect(typeof initialContext.shouldTriggerAutoBackup).toBe('boolean');
});
it('should have isCloudSignedIn as false', () => {
expect(initialContext.isCloudSignedIn).toBe(false);
expect(typeof initialContext.isCloudSignedIn).toBe('boolean');
});
it('should have all 6 required properties', () => {
const properties = Object.keys(initialContext);
expect(properties).toHaveLength(6);
});
});
describe('String properties', () => {
const context = backupAndRestoreSetupModel.initialContext;
it('errorMessage should be empty', () => {
expect(context.errorMessage).toBe('');
expect(typeof context.errorMessage).toBe('string');
});
});
describe('Object properties', () => {
const context = backupAndRestoreSetupModel.initialContext;
it('serviceRefs should be empty object', () => {
expect(typeof context.serviceRefs).toBe('object');
expect(Object.keys(context.serviceRefs)).toHaveLength(0);
});
});
describe('Boolean properties', () => {
const context = backupAndRestoreSetupModel.initialContext;
it('isLoading should be false', () => {
expect(context.isLoading).toBe(false);
expect(typeof context.isLoading).toBe('boolean');
});
it('shouldTriggerAutoBackup should be false', () => {
expect(context.shouldTriggerAutoBackup).toBe(false);
expect(typeof context.shouldTriggerAutoBackup).toBe('boolean');
});
it('isCloudSignedIn should be false', () => {
expect(context.isCloudSignedIn).toBe(false);
expect(typeof context.isCloudSignedIn).toBe('boolean');
});
it('should have correct initial values for boolean properties', () => {
const falseProps = [
context.isLoading,
context.shouldTriggerAutoBackup,
context.isCloudSignedIn,
];
falseProps.forEach(prop => {
expect(prop).toBe(false);
expect(typeof prop).toBe('boolean');
});
});
});
describe('Undefined properties', () => {
const context = backupAndRestoreSetupModel.initialContext;
it('profileInfo should be undefined', () => {
expect(context.profileInfo).toBeUndefined();
});
});
describe('Model events', () => {
it('should have events object', () => {
expect(backupAndRestoreSetupModel.events).toBeDefined();
expect(typeof backupAndRestoreSetupModel.events).toBe('object');
});
it('should have event creators', () => {
const eventKeys = Object.keys(backupAndRestoreSetupModel.events);
expect(eventKeys.length).toBeGreaterThan(0);
});
});
describe('Property types validation', () => {
const context = backupAndRestoreSetupModel.initialContext;
it('should have correct types for all properties', () => {
expect(typeof context.isLoading).toBe('boolean');
expect(context.profileInfo).toBeUndefined();
expect(typeof context.errorMessage).toBe('string');
expect(typeof context.serviceRefs).toBe('object');
expect(typeof context.shouldTriggerAutoBackup).toBe('boolean');
expect(typeof context.isCloudSignedIn).toBe('boolean');
});
});
});

View File

@@ -0,0 +1,289 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
selectIsLoading,
selectProfileInfo,
selectIsNetworkError,
selectShouldTriggerAutoBackup,
selectShowAccountSelectionConfirmation,
selectIsSigningIn,
selectIsSigningInSuccessful,
selectIsSigningFailure,
selectIsCloudSignedInFailed,
} from './backupAndRestoreSetupSelectors';
describe('backupAndRestoreSetupSelectors', () => {
const mockProfileInfo = {
email: 'test@example.com',
name: 'Test User',
id: 'user123',
};
const mockState: any = {
context: {
isLoading: false,
profileInfo: mockProfileInfo,
shouldTriggerAutoBackup: true,
},
matches: jest.fn(() => false),
};
describe('selectIsLoading', () => {
it('should return loading status from context', () => {
const result = selectIsLoading(mockState);
expect(result).toBe(false);
});
it('should return true when loading', () => {
const state: any = {
...mockState,
context: {...mockState.context, isLoading: true},
};
const result = selectIsLoading(state);
expect(result).toBe(true);
});
});
describe('selectProfileInfo', () => {
it('should return profile info from context', () => {
const result = selectProfileInfo(mockState);
expect(result).toEqual(mockProfileInfo);
});
it('should return profile with all properties', () => {
const result = selectProfileInfo(mockState);
expect(result).toHaveProperty('email');
expect(result).toHaveProperty('name');
expect(result).toHaveProperty('id');
});
it('should handle null profile info', () => {
const state: any = {
...mockState,
context: {...mockState.context, profileInfo: null},
};
const result = selectProfileInfo(state);
expect(result).toBeNull();
});
});
describe('selectIsNetworkError', () => {
it('should return true when in init.noInternet state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'init.noInternet'),
};
const result = selectIsNetworkError(state);
expect(result).toBe(true);
});
it('should return true when in checkSignIn.noInternet state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'checkSignIn.noInternet'),
};
const result = selectIsNetworkError(state);
expect(result).toBe(true);
});
it('should return true when in signIn.noInternet state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'signIn.noInternet'),
};
const result = selectIsNetworkError(state);
expect(result).toBe(true);
});
it('should return false when not in any noInternet state', () => {
const result = selectIsNetworkError(mockState);
expect(result).toBe(false);
});
it('should call matches with all three network error states', () => {
const state: any = {
...mockState,
matches: jest.fn(() => false),
};
selectIsNetworkError(state);
expect(state.matches).toHaveBeenCalledWith('init.noInternet');
expect(state.matches).toHaveBeenCalledWith('checkSignIn.noInternet');
expect(state.matches).toHaveBeenCalledWith('signIn.noInternet');
});
});
describe('selectShouldTriggerAutoBackup', () => {
it('should return auto backup trigger flag', () => {
const result = selectShouldTriggerAutoBackup(mockState);
expect(result).toBe(true);
});
it('should return false when auto backup should not trigger', () => {
const state: any = {
...mockState,
context: {...mockState.context, shouldTriggerAutoBackup: false},
};
const result = selectShouldTriggerAutoBackup(state);
expect(result).toBe(false);
});
});
describe('selectShowAccountSelectionConfirmation', () => {
it('should return true when in selectCloudAccount state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'selectCloudAccount'),
};
const result = selectShowAccountSelectionConfirmation(state);
expect(result).toBe(true);
});
it('should call matches with selectCloudAccount', () => {
const state: any = {
...mockState,
matches: jest.fn(() => false),
};
selectShowAccountSelectionConfirmation(state);
expect(state.matches).toHaveBeenCalledWith('selectCloudAccount');
});
it('should return false when not in selectCloudAccount state', () => {
const result = selectShowAccountSelectionConfirmation(mockState);
expect(result).toBe(false);
});
});
describe('selectIsSigningIn', () => {
it('should return true when in signIn state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'signIn'),
};
const result = selectIsSigningIn(state);
expect(result).toBe(true);
});
it('should call matches with signIn', () => {
const state: any = {
...mockState,
matches: jest.fn(() => false),
};
selectIsSigningIn(state);
expect(state.matches).toHaveBeenCalledWith('signIn');
});
it('should return false when not signing in', () => {
const result = selectIsSigningIn(mockState);
expect(result).toBe(false);
});
});
describe('selectIsSigningInSuccessful', () => {
it('should return true when in backupAndRestore state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'backupAndRestore'),
};
const result = selectIsSigningInSuccessful(state);
expect(result).toBe(true);
});
it('should call matches with backupAndRestore', () => {
const state: any = {
...mockState,
matches: jest.fn(() => false),
};
selectIsSigningInSuccessful(state);
expect(state.matches).toHaveBeenCalledWith('backupAndRestore');
});
it('should return false when sign in not successful', () => {
const result = selectIsSigningInSuccessful(mockState);
expect(result).toBe(false);
});
});
describe('selectIsSigningFailure', () => {
it('should return true when in signIn.error state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'signIn.error'),
};
const result = selectIsSigningFailure(state);
expect(result).toBe(true);
});
it('should return true when in checkSignIn.error state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'checkSignIn.error'),
};
const result = selectIsSigningFailure(state);
expect(result).toBe(true);
});
it('should return false when not in error state', () => {
const result = selectIsSigningFailure(mockState);
expect(result).toBe(false);
});
it('should call matches with both error states', () => {
const state: any = {
...mockState,
matches: jest.fn(() => false),
};
selectIsSigningFailure(state);
expect(state.matches).toHaveBeenCalledWith('signIn.error');
expect(state.matches).toHaveBeenCalledWith('checkSignIn.error');
});
});
describe('selectIsCloudSignedInFailed', () => {
it('should return true when in checkSignIn.error state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'checkSignIn.error'),
};
const result = selectIsCloudSignedInFailed(state);
expect(result).toBe(true);
});
it('should call matches with checkSignIn.error', () => {
const state: any = {
...mockState,
matches: jest.fn(() => false),
};
selectIsCloudSignedInFailed(state);
expect(state.matches).toHaveBeenCalledWith('checkSignIn.error');
});
it('should return false when cloud sign in did not fail', () => {
const result = selectIsCloudSignedInFailed(mockState);
expect(result).toBe(false);
});
});
describe('Edge cases', () => {
it('should handle empty profile info', () => {
const state: any = {
...mockState,
context: {...mockState.context, profileInfo: {}},
};
const result = selectProfileInfo(state);
expect(result).toEqual({});
});
it('should handle undefined values', () => {
const state: any = {
...mockState,
context: {
isLoading: undefined,
profileInfo: undefined,
shouldTriggerAutoBackup: undefined,
},
};
expect(selectIsLoading(state)).toBeUndefined();
expect(selectProfileInfo(state)).toBeUndefined();
expect(selectShouldTriggerAutoBackup(state)).toBeUndefined();
});
});
});

View File

@@ -0,0 +1,114 @@
import {restoreModel} from './restoreModel';
describe('restoreModel', () => {
describe('Model structure', () => {
it('should be defined', () => {
expect(restoreModel).toBeDefined();
});
it('should have initialContext', () => {
expect(restoreModel.initialContext).toBeDefined();
});
it('should have events', () => {
expect(restoreModel.events).toBeDefined();
});
});
describe('Initial Context', () => {
const initialContext = restoreModel.initialContext;
it('should have serviceRefs as empty object', () => {
expect(initialContext.serviceRefs).toEqual({});
expect(typeof initialContext.serviceRefs).toBe('object');
});
it('should have fileName as empty string', () => {
expect(initialContext.fileName).toBe('');
expect(typeof initialContext.fileName).toBe('string');
});
it('should have dataFromBackupFile as empty object', () => {
expect(initialContext.dataFromBackupFile).toEqual({});
expect(typeof initialContext.dataFromBackupFile).toBe('object');
});
it('should have errorReason as empty string', () => {
expect(initialContext.errorReason).toBe('');
expect(typeof initialContext.errorReason).toBe('string');
});
it('should have showRestoreInProgress as false', () => {
expect(initialContext.showRestoreInProgress).toBe(false);
expect(typeof initialContext.showRestoreInProgress).toBe('boolean');
});
it('should have all 5 required properties', () => {
const properties = Object.keys(initialContext);
expect(properties).toHaveLength(5);
});
});
describe('String properties', () => {
const context = restoreModel.initialContext;
it('all empty string properties should be empty', () => {
const emptyStrings = [context.fileName, context.errorReason];
emptyStrings.forEach(str => {
expect(str).toBe('');
expect(typeof str).toBe('string');
});
});
});
describe('Object properties', () => {
const context = restoreModel.initialContext;
it('all empty object properties should be empty objects', () => {
const emptyObjects = [context.serviceRefs, context.dataFromBackupFile];
emptyObjects.forEach(obj => {
expect(typeof obj).toBe('object');
expect(Object.keys(obj)).toHaveLength(0);
});
});
});
describe('Boolean properties', () => {
const context = restoreModel.initialContext;
it('showRestoreInProgress should be false', () => {
expect(context.showRestoreInProgress).toBe(false);
expect(typeof context.showRestoreInProgress).toBe('boolean');
});
it('should have correct initial values for boolean properties', () => {
expect(context.showRestoreInProgress).toBe(false);
});
});
describe('Model events', () => {
it('should have events object', () => {
expect(restoreModel.events).toBeDefined();
expect(typeof restoreModel.events).toBe('object');
});
it('should have event creators', () => {
const eventKeys = Object.keys(restoreModel.events);
expect(eventKeys.length).toBeGreaterThan(0);
});
});
describe('Property types validation', () => {
const context = restoreModel.initialContext;
it('should have correct types for all properties', () => {
expect(typeof context.serviceRefs).toBe('object');
expect(typeof context.fileName).toBe('string');
expect(typeof context.dataFromBackupFile).toBe('object');
expect(typeof context.errorReason).toBe('string');
expect(typeof context.showRestoreInProgress).toBe('boolean');
});
});
});

View File

@@ -0,0 +1,125 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
selectErrorReason,
selectIsBackUpRestoring,
selectIsBackUpRestoreSuccess,
selectIsBackUpRestoreFailure,
selectShowRestoreInProgress,
} from './restoreSelector';
describe('restoreSelector', () => {
describe('selectErrorReason', () => {
it('should return errorReason from context', () => {
const mockState: any = {
context: {
errorReason: 'Failed to restore backup',
},
};
expect(selectErrorReason(mockState)).toBe('Failed to restore backup');
});
it('should return null when no error', () => {
const mockState: any = {
context: {
errorReason: null,
},
};
expect(selectErrorReason(mockState)).toBeNull();
});
});
describe('selectIsBackUpRestoring', () => {
it('should return true when in restoreBackup state but not success or failure', () => {
const mockState: any = {
matches: jest.fn((state: string) => {
if (state === 'restoreBackup') return true;
if (state === 'restoreBackup.success') return false;
if (state === 'restoreBackup.failure') return false;
return false;
}),
};
expect(selectIsBackUpRestoring(mockState)).toBe(true);
});
it('should return false when in restoreBackup.success state', () => {
const mockState: any = {
matches: jest.fn((state: string) => {
if (state === 'restoreBackup') return true;
if (state === 'restoreBackup.success') return true;
return false;
}),
};
expect(selectIsBackUpRestoring(mockState)).toBe(false);
});
it('should return false when in restoreBackup.failure state', () => {
const mockState: any = {
matches: jest.fn((state: string) => {
if (state === 'restoreBackup') return true;
if (state === 'restoreBackup.failure') return true;
return false;
}),
};
expect(selectIsBackUpRestoring(mockState)).toBe(false);
});
it('should return false when not in restoreBackup state', () => {
const mockState: any = {
matches: jest.fn(() => false),
};
expect(selectIsBackUpRestoring(mockState)).toBe(false);
});
});
describe('selectIsBackUpRestoreSuccess', () => {
it('should return true when in restoreBackup.success state', () => {
const mockState: any = {
matches: jest.fn((state: string) => state === 'restoreBackup.success'),
};
expect(selectIsBackUpRestoreSuccess(mockState)).toBe(true);
});
it('should return false when not in success state', () => {
const mockState: any = {
matches: jest.fn(() => false),
};
expect(selectIsBackUpRestoreSuccess(mockState)).toBe(false);
});
});
describe('selectIsBackUpRestoreFailure', () => {
it('should return true when in restoreBackup.failure state', () => {
const mockState: any = {
matches: jest.fn((state: string) => state === 'restoreBackup.failure'),
};
expect(selectIsBackUpRestoreFailure(mockState)).toBe(true);
});
it('should return false when not in failure state', () => {
const mockState: any = {
matches: jest.fn(() => false),
};
expect(selectIsBackUpRestoreFailure(mockState)).toBe(false);
});
});
describe('selectShowRestoreInProgress', () => {
it('should return showRestoreInProgress from context', () => {
const mockState: any = {
context: {
showRestoreInProgress: true,
},
};
expect(selectShowRestoreInProgress(mockState)).toBe(true);
});
it('should return false when showRestoreInProgress is false', () => {
const mockState: any = {
context: {
showRestoreInProgress: false,
},
};
expect(selectShowRestoreInProgress(mockState)).toBe(false);
});
});
});

View File

@@ -0,0 +1,298 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
selectIsCancelling,
selectIsReviewing,
selectIsAccepted,
selectIsRejected,
selectIsVerifyingIdentity,
selectIsInvalidIdentity,
selectIsDisconnected,
selectIsBluetoothDenied,
selectBleError,
selectIsExchangingDeviceInfo,
selectIsExchangingDeviceInfoTimeout,
selectIsOffline,
selectIsHandlingBleError,
selectReadyForBluetoothStateCheck,
selectIsNearByDevicesPermissionDenied,
selectIsBluetoothPermissionDenied,
selectIsStartPermissionCheck,
selectIsLocationPermissionRationale,
} from './commonSelectors';
describe('commonSelectors', () => {
const mockState: any = {
context: {
bleError: null,
readyForBluetoothStateCheck: false,
},
matches: jest.fn(() => false),
};
describe('selectIsCancelling', () => {
it('should return true when in cancelling state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'cancelling'),
};
expect(selectIsCancelling(state)).toBe(true);
});
it('should return false when not cancelling', () => {
expect(selectIsCancelling(mockState)).toBe(false);
});
});
describe('selectIsReviewing', () => {
it('should return true when in reviewing state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'reviewing'),
};
expect(selectIsReviewing(state)).toBe(true);
});
it('should return false when not reviewing', () => {
expect(selectIsReviewing(mockState)).toBe(false);
});
});
describe('selectIsAccepted', () => {
it('should return true when in reviewing.accepted state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'reviewing.accepted'),
};
expect(selectIsAccepted(state)).toBe(true);
});
it('should call matches with reviewing.accepted', () => {
selectIsAccepted(mockState);
expect(mockState.matches).toHaveBeenCalledWith('reviewing.accepted');
});
});
describe('selectIsRejected', () => {
it('should return true when in reviewing.rejected state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'reviewing.rejected'),
};
expect(selectIsRejected(state)).toBe(true);
});
it('should call matches with reviewing.rejected', () => {
selectIsRejected(mockState);
expect(mockState.matches).toHaveBeenCalledWith('reviewing.rejected');
});
});
describe('selectIsVerifyingIdentity', () => {
it('should return true when in reviewing.verifyingIdentity state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'reviewing.verifyingIdentity'),
};
expect(selectIsVerifyingIdentity(state)).toBe(true);
});
it('should call matches with reviewing.verifyingIdentity', () => {
selectIsVerifyingIdentity(mockState);
expect(mockState.matches).toHaveBeenCalledWith(
'reviewing.verifyingIdentity',
);
});
});
describe('selectIsInvalidIdentity', () => {
it('should return true when in reviewing.invalidIdentity state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'reviewing.invalidIdentity'),
};
expect(selectIsInvalidIdentity(state)).toBe(true);
});
it('should call matches with reviewing.invalidIdentity', () => {
selectIsInvalidIdentity(mockState);
expect(mockState.matches).toHaveBeenCalledWith(
'reviewing.invalidIdentity',
);
});
});
describe('selectIsDisconnected', () => {
it('should return true when in disconnected state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'disconnected'),
};
expect(selectIsDisconnected(state)).toBe(true);
});
it('should call matches with disconnected', () => {
selectIsDisconnected(mockState);
expect(mockState.matches).toHaveBeenCalledWith('disconnected');
});
});
describe('selectIsBluetoothDenied', () => {
it('should return true when in bluetoothDenied state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'bluetoothDenied'),
};
expect(selectIsBluetoothDenied(state)).toBe(true);
});
it('should call matches with bluetoothDenied', () => {
selectIsBluetoothDenied(mockState);
expect(mockState.matches).toHaveBeenCalledWith('bluetoothDenied');
});
});
describe('selectBleError', () => {
it('should return BLE error from context', () => {
const bleError = {code: 'BLE_001', message: 'Connection failed'};
const state: any = {
...mockState,
context: {...mockState.context, bleError},
};
expect(selectBleError(state)).toEqual(bleError);
});
it('should return null when no BLE error', () => {
expect(selectBleError(mockState)).toBeNull();
});
});
describe('TODO selectors (hardcoded)', () => {
it('selectIsExchangingDeviceInfo should always return false', () => {
expect(selectIsExchangingDeviceInfo()).toBe(false);
});
it('selectIsExchangingDeviceInfoTimeout should always return false', () => {
expect(selectIsExchangingDeviceInfoTimeout()).toBe(false);
});
it('selectIsOffline should always return false', () => {
expect(selectIsOffline()).toBe(false);
});
});
describe('selectIsHandlingBleError', () => {
it('should return true when in handlingBleError state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'handlingBleError'),
};
expect(selectIsHandlingBleError(state)).toBe(true);
});
it('should call matches with handlingBleError', () => {
selectIsHandlingBleError(mockState);
expect(mockState.matches).toHaveBeenCalledWith('handlingBleError');
});
});
describe('selectReadyForBluetoothStateCheck', () => {
it('should return ready status from context', () => {
expect(selectReadyForBluetoothStateCheck(mockState)).toBe(false);
});
it('should return true when ready', () => {
const state: any = {
...mockState,
context: {...mockState.context, readyForBluetoothStateCheck: true},
};
expect(selectReadyForBluetoothStateCheck(state)).toBe(true);
});
});
describe('selectIsNearByDevicesPermissionDenied', () => {
it('should return true when in nearByDevicesPermissionDenied state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'nearByDevicesPermissionDenied'),
};
expect(selectIsNearByDevicesPermissionDenied(state)).toBe(true);
});
it('should call matches with nearByDevicesPermissionDenied', () => {
selectIsNearByDevicesPermissionDenied(mockState);
expect(mockState.matches).toHaveBeenCalledWith(
'nearByDevicesPermissionDenied',
);
});
});
describe('selectIsBluetoothPermissionDenied', () => {
it('should return true when in bluetoothPermissionDenied state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'bluetoothPermissionDenied'),
};
expect(selectIsBluetoothPermissionDenied(state)).toBe(true);
});
it('should call matches with bluetoothPermissionDenied', () => {
selectIsBluetoothPermissionDenied(mockState);
expect(mockState.matches).toHaveBeenCalledWith(
'bluetoothPermissionDenied',
);
});
});
describe('selectIsStartPermissionCheck', () => {
it('should return true when in startPermissionCheck state', () => {
const state: any = {
...mockState,
matches: jest.fn((s: string) => s === 'startPermissionCheck'),
};
expect(selectIsStartPermissionCheck(state)).toBe(true);
});
it('should call matches with startPermissionCheck', () => {
selectIsStartPermissionCheck(mockState);
expect(mockState.matches).toHaveBeenCalledWith('startPermissionCheck');
});
});
describe('selectIsLocationPermissionRationale', () => {
it('should return true when in checkingLocationState.LocationPermissionRationale state', () => {
const state: any = {
...mockState,
matches: jest.fn(
(s: string) =>
s === 'checkingLocationState.LocationPermissionRationale',
),
};
expect(selectIsLocationPermissionRationale(state)).toBe(true);
});
it('should call matches with correct state path', () => {
selectIsLocationPermissionRationale(mockState);
expect(mockState.matches).toHaveBeenCalledWith(
'checkingLocationState.LocationPermissionRationale',
);
});
});
describe('Edge cases', () => {
it('should handle undefined bleError', () => {
const state: any = {
...mockState,
context: {...mockState.context, bleError: undefined},
};
expect(selectBleError(state)).toBeUndefined();
});
it('should handle empty bleError object', () => {
const state: any = {
...mockState,
context: {...mockState.context, bleError: {}},
};
expect(selectBleError(state)).toEqual({});
});
});
});

View File

@@ -0,0 +1,233 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
selectSenderInfo,
selectCredential,
selectVerifiableCredentialData,
selectIsReviewingInIdle,
selectIsWaitingForConnection,
selectIsCheckingBluetoothService,
selectIsWaitingForVc,
selectIsWaitingForVcTimeout,
selectOpenId4VpUri,
selectIsAccepting,
selectIsDisplayingIncomingVC,
selectIsSavingFailedInIdle,
selectIsDone,
selectIsNavigatingToReceivedCards,
selectIsNavigatingToHome,
} from './selectors';
describe('requestMachine selectors', () => {
describe('selectSenderInfo', () => {
it('should return senderInfo from context', () => {
const mockSenderInfo = {name: 'John Doe', deviceId: 'device-123'};
const mockState: any = {
context: {
senderInfo: mockSenderInfo,
},
};
expect(selectSenderInfo(mockState)).toBe(mockSenderInfo);
});
});
describe('selectCredential', () => {
it('should return verifiableCredential from incomingVc', () => {
const mockVC = {credential: {id: 'cred-123'}};
const mockState: any = {
context: {
incomingVc: {
verifiableCredential: mockVC,
},
},
};
expect(selectCredential(mockState)).toBe(mockVC);
});
});
describe('selectVerifiableCredentialData', () => {
it('should return formatted verifiable credential data', () => {
const mockState: any = {
context: {
incomingVc: {
vcMetadata: {
id: 'vc-001',
issuer: 'Test Issuer',
},
verifiableCredential: {
credential: {
credentialSubject: {
face: 'base64-face-data',
},
},
issuerLogo: 'https://example.com/logo.png',
wellKnown: 'https://example.com/.well-known',
credentialConfigurationId: 'config-123',
},
},
},
};
const result = selectVerifiableCredentialData(mockState);
expect(result.issuer).toBe('Test Issuer');
expect(result.issuerLogo).toBe('https://example.com/logo.png');
expect(result.wellKnown).toBe('https://example.com/.well-known');
expect(result.credentialConfigurationId).toBe('config-123');
expect(result.face).toBe('base64-face-data');
});
it('should use biometrics face when credentialSubject face is not available', () => {
const mockState: any = {
context: {
incomingVc: {
vcMetadata: {
id: 'vc-002',
issuer: 'Mosip',
},
credential: {
biometrics: {
face: 'biometric-face-data',
},
},
verifiableCredential: {},
},
},
};
const result = selectVerifiableCredentialData(mockState);
expect(result.face).toBe('biometric-face-data');
});
});
describe('selectIsReviewingInIdle', () => {
it('should return true when in reviewing.idle state', () => {
const mockState: any = {
matches: jest.fn((state: string) => state === 'reviewing.idle'),
};
expect(selectIsReviewingInIdle(mockState)).toBe(true);
});
it('should return false when not in reviewing.idle state', () => {
const mockState: any = {
matches: jest.fn(() => false),
};
expect(selectIsReviewingInIdle(mockState)).toBe(false);
});
});
describe('selectIsWaitingForConnection', () => {
it('should return true when in waitingForConnection state', () => {
const mockState: any = {
matches: jest.fn((state: string) => state === 'waitingForConnection'),
};
expect(selectIsWaitingForConnection(mockState)).toBe(true);
});
});
describe('selectIsCheckingBluetoothService', () => {
it('should return true when in checkingBluetoothService state', () => {
const mockState: any = {
matches: jest.fn(
(state: string) => state === 'checkingBluetoothService',
),
};
expect(selectIsCheckingBluetoothService(mockState)).toBe(true);
});
});
describe('selectIsWaitingForVc', () => {
it('should return true when in waitingForVc.inProgress state', () => {
const mockState: any = {
matches: jest.fn(
(state: string) => state === 'waitingForVc.inProgress',
),
};
expect(selectIsWaitingForVc(mockState)).toBe(true);
});
});
describe('selectIsWaitingForVcTimeout', () => {
it('should return true when in waitingForVc.timeout state', () => {
const mockState: any = {
matches: jest.fn((state: string) => state === 'waitingForVc.timeout'),
};
expect(selectIsWaitingForVcTimeout(mockState)).toBe(true);
});
});
describe('selectOpenId4VpUri', () => {
it('should return openId4VpUri from context', () => {
const mockState: any = {
context: {
openId4VpUri: 'openid4vp://verify?request=abc123',
},
};
expect(selectOpenId4VpUri(mockState)).toBe(
'openid4vp://verify?request=abc123',
);
});
});
describe('selectIsAccepting', () => {
it('should return true when in reviewing.accepting state', () => {
const mockState: any = {
matches: jest.fn((state: string) => state === 'reviewing.accepting'),
};
expect(selectIsAccepting(mockState)).toBe(true);
});
});
describe('selectIsDisplayingIncomingVC', () => {
it('should return true when in reviewing.displayingIncomingVC state', () => {
const mockState: any = {
matches: jest.fn(
(state: string) => state === 'reviewing.displayingIncomingVC',
),
};
expect(selectIsDisplayingIncomingVC(mockState)).toBe(true);
});
});
describe('selectIsSavingFailedInIdle', () => {
it('should return true when in reviewing.savingFailed.idle state', () => {
const mockState: any = {
matches: jest.fn(
(state: string) => state === 'reviewing.savingFailed.idle',
),
};
expect(selectIsSavingFailedInIdle(mockState)).toBe(true);
});
});
describe('selectIsDone', () => {
it('should return true when in reviewing.navigatingToHistory state', () => {
const mockState: any = {
matches: jest.fn(
(state: string) => state === 'reviewing.navigatingToHistory',
),
};
expect(selectIsDone(mockState)).toBe(true);
});
});
describe('selectIsNavigatingToReceivedCards', () => {
it('should return true when in reviewing.navigatingToReceivedCards state', () => {
const mockState: any = {
matches: jest.fn(
(state: string) => state === 'reviewing.navigatingToReceivedCards',
),
};
expect(selectIsNavigatingToReceivedCards(mockState)).toBe(true);
});
});
describe('selectIsNavigatingToHome', () => {
it('should return true when in reviewing.navigatingToHome state', () => {
const mockState: any = {
matches: jest.fn(
(state: string) => state === 'reviewing.navigatingToHome',
),
};
expect(selectIsNavigatingToHome(mockState)).toBe(true);
});
});
});

View File

@@ -0,0 +1,292 @@
import {ScanModel} from './scanModel';
import {VCShareFlowType} from '../../../shared/Utils';
describe('ScanModel', () => {
describe('Model structure', () => {
it('should be defined', () => {
expect(ScanModel).toBeDefined();
});
it('should have initialContext', () => {
expect(ScanModel.initialContext).toBeDefined();
});
it('should have events', () => {
expect(ScanModel.events).toBeDefined();
});
});
describe('Initial Context', () => {
const initialContext = ScanModel.initialContext;
it('should have serviceRefs as empty object', () => {
expect(initialContext.serviceRefs).toEqual({});
expect(typeof initialContext.serviceRefs).toBe('object');
});
it('should have senderInfo as empty object', () => {
expect(initialContext.senderInfo).toEqual({});
expect(typeof initialContext.senderInfo).toBe('object');
});
it('should have receiverInfo as empty object', () => {
expect(initialContext.receiverInfo).toEqual({});
expect(typeof initialContext.receiverInfo).toBe('object');
});
it('should have selectedVc as empty object', () => {
expect(initialContext.selectedVc).toEqual({});
expect(typeof initialContext.selectedVc).toBe('object');
});
it('should have bleError as empty object', () => {
expect(initialContext.bleError).toEqual({});
expect(typeof initialContext.bleError).toBe('object');
});
it('should have loggers as empty array', () => {
expect(initialContext.loggers).toEqual([]);
expect(Array.isArray(initialContext.loggers)).toBe(true);
});
it('should have vcName as empty string', () => {
expect(initialContext.vcName).toBe('');
expect(typeof initialContext.vcName).toBe('string');
});
it('should have flowType as SIMPLE_SHARE', () => {
expect(initialContext.flowType).toBe(VCShareFlowType.SIMPLE_SHARE);
});
it('should have openID4VPFlowType as empty string', () => {
expect(initialContext.openID4VPFlowType).toBe('');
expect(typeof initialContext.openID4VPFlowType).toBe('string');
});
it('should have verificationImage as empty object', () => {
expect(initialContext.verificationImage).toEqual({});
expect(typeof initialContext.verificationImage).toBe('object');
});
it('should have openId4VpUri as empty string', () => {
expect(initialContext.openId4VpUri).toBe('');
expect(typeof initialContext.openId4VpUri).toBe('string');
});
it('should have shareLogType as empty string', () => {
expect(initialContext.shareLogType).toBe('');
expect(typeof initialContext.shareLogType).toBe('string');
});
it('should have QrLoginRef as empty object', () => {
expect(initialContext.QrLoginRef).toEqual({});
expect(typeof initialContext.QrLoginRef).toBe('object');
});
it('should have OpenId4VPRef as empty object', () => {
expect(initialContext.OpenId4VPRef).toEqual({});
expect(typeof initialContext.OpenId4VPRef).toBe('object');
});
it('should have showQuickShareSuccessBanner as false', () => {
expect(initialContext.showQuickShareSuccessBanner).toBe(false);
expect(typeof initialContext.showQuickShareSuccessBanner).toBe('boolean');
});
it('should have linkCode as empty string', () => {
expect(initialContext.linkCode).toBe('');
expect(typeof initialContext.linkCode).toBe('string');
});
it('should have authorizationRequest as empty string', () => {
expect(initialContext.authorizationRequest).toBe('');
expect(typeof initialContext.authorizationRequest).toBe('string');
});
it('should have quickShareData as empty object', () => {
expect(initialContext.quickShareData).toEqual({});
expect(typeof initialContext.quickShareData).toBe('object');
});
it('should have isQrLoginViaDeepLink as false', () => {
expect(initialContext.isQrLoginViaDeepLink).toBe(false);
expect(typeof initialContext.isQrLoginViaDeepLink).toBe('boolean');
});
it('should have isOVPViaDeepLink as false', () => {
expect(initialContext.isOVPViaDeepLink).toBe(false);
expect(typeof initialContext.isOVPViaDeepLink).toBe('boolean');
});
it('should have showFaceAuthConsent as true', () => {
expect(initialContext.showFaceAuthConsent).toBe(true);
expect(typeof initialContext.showFaceAuthConsent).toBe('boolean');
});
it('should have readyForBluetoothStateCheck as false', () => {
expect(initialContext.readyForBluetoothStateCheck).toBe(false);
expect(typeof initialContext.readyForBluetoothStateCheck).toBe('boolean');
});
it('should have showFaceCaptureSuccessBanner as false', () => {
expect(initialContext.showFaceCaptureSuccessBanner).toBe(false);
expect(typeof initialContext.showFaceCaptureSuccessBanner).toBe(
'boolean',
);
});
it('should have all 23 required properties', () => {
const properties = Object.keys(initialContext);
expect(properties).toHaveLength(23);
});
});
describe('String properties', () => {
const context = ScanModel.initialContext;
it('all empty string properties should be empty', () => {
const emptyStrings = [
context.vcName,
context.openID4VPFlowType,
context.openId4VpUri,
context.shareLogType,
context.linkCode,
context.authorizationRequest,
];
emptyStrings.forEach(str => {
expect(str).toBe('');
expect(typeof str).toBe('string');
});
});
it('flowType should be SIMPLE_SHARE enum value', () => {
expect(context.flowType).toBe(VCShareFlowType.SIMPLE_SHARE);
});
});
describe('Array properties', () => {
const context = ScanModel.initialContext;
it('loggers should be empty array', () => {
expect(Array.isArray(context.loggers)).toBe(true);
expect(context.loggers).toHaveLength(0);
});
});
describe('Object properties', () => {
const context = ScanModel.initialContext;
it('all empty object properties should be empty objects', () => {
const emptyObjects = [
context.serviceRefs,
context.senderInfo,
context.receiverInfo,
context.selectedVc,
context.bleError,
context.verificationImage,
context.QrLoginRef,
context.OpenId4VPRef,
context.quickShareData,
];
emptyObjects.forEach(obj => {
expect(typeof obj).toBe('object');
expect(Object.keys(obj)).toHaveLength(0);
});
});
});
describe('Boolean properties', () => {
const context = ScanModel.initialContext;
it('showQuickShareSuccessBanner should be false', () => {
expect(context.showQuickShareSuccessBanner).toBe(false);
expect(typeof context.showQuickShareSuccessBanner).toBe('boolean');
});
it('isQrLoginViaDeepLink should be false', () => {
expect(context.isQrLoginViaDeepLink).toBe(false);
expect(typeof context.isQrLoginViaDeepLink).toBe('boolean');
});
it('isOVPViaDeepLink should be false', () => {
expect(context.isOVPViaDeepLink).toBe(false);
expect(typeof context.isOVPViaDeepLink).toBe('boolean');
});
it('showFaceAuthConsent should be true', () => {
expect(context.showFaceAuthConsent).toBe(true);
expect(typeof context.showFaceAuthConsent).toBe('boolean');
});
it('readyForBluetoothStateCheck should be false', () => {
expect(context.readyForBluetoothStateCheck).toBe(false);
expect(typeof context.readyForBluetoothStateCheck).toBe('boolean');
});
it('showFaceCaptureSuccessBanner should be false', () => {
expect(context.showFaceCaptureSuccessBanner).toBe(false);
expect(typeof context.showFaceCaptureSuccessBanner).toBe('boolean');
});
it('should have correct initial values for boolean properties', () => {
const falseProps = [
context.showQuickShareSuccessBanner,
context.isQrLoginViaDeepLink,
context.isOVPViaDeepLink,
context.readyForBluetoothStateCheck,
context.showFaceCaptureSuccessBanner,
];
falseProps.forEach(prop => {
expect(prop).toBe(false);
expect(typeof prop).toBe('boolean');
});
expect(context.showFaceAuthConsent).toBe(true);
expect(typeof context.showFaceAuthConsent).toBe('boolean');
});
});
describe('Model events', () => {
it('should have events object', () => {
expect(ScanModel.events).toBeDefined();
expect(typeof ScanModel.events).toBe('object');
});
it('should have event creators', () => {
const eventKeys = Object.keys(ScanModel.events);
expect(eventKeys.length).toBeGreaterThan(0);
});
});
describe('Property types validation', () => {
const context = ScanModel.initialContext;
it('should have correct types for all properties', () => {
expect(typeof context.vcName).toBe('string');
expect(typeof context.openID4VPFlowType).toBe('string');
expect(typeof context.openId4VpUri).toBe('string');
expect(typeof context.shareLogType).toBe('string');
expect(typeof context.linkCode).toBe('string');
expect(typeof context.authorizationRequest).toBe('string');
expect(Array.isArray(context.loggers)).toBe(true);
expect(typeof context.showQuickShareSuccessBanner).toBe('boolean');
expect(typeof context.isQrLoginViaDeepLink).toBe('boolean');
expect(typeof context.isOVPViaDeepLink).toBe('boolean');
expect(typeof context.showFaceAuthConsent).toBe('boolean');
expect(typeof context.readyForBluetoothStateCheck).toBe('boolean');
expect(typeof context.showFaceCaptureSuccessBanner).toBe('boolean');
expect(typeof context.serviceRefs).toBe('object');
expect(typeof context.senderInfo).toBe('object');
expect(typeof context.receiverInfo).toBe('object');
expect(typeof context.selectedVc).toBe('object');
expect(typeof context.bleError).toBe('object');
expect(typeof context.verificationImage).toBe('object');
expect(typeof context.QrLoginRef).toBe('object');
expect(typeof context.OpenId4VPRef).toBe('object');
expect(typeof context.quickShareData).toBe('object');
});
});
});

View File

@@ -0,0 +1,500 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
selectFlowType,
selectOpenID4VPFlowType,
selectReceiverInfo,
selectVcName,
selectCredential,
selectVerifiableCredentialData,
selectQrLoginRef,
selectIsScanning,
selectIsQuickShareDone,
selectShowQuickShareSuccessBanner,
selectIsConnecting,
selectIsConnectingTimeout,
selectIsSelectingVc,
selectIsSendingVc,
selectIsSendingVP,
selectIsSendingVPError,
selectIsSendingVPSuccess,
selectIsFaceIdentityVerified,
selectIsSendingVcTimeout,
selectIsSendingVPTimeout,
selectIsSent,
selectIsInvalid,
selectIsLocationDenied,
selectIsLocationDisabled,
selectIsShowQrLogin,
selectIsQrLoginDone,
selectIsQrLoginDoneViaDeeplink,
selectIsQrLoginStoring,
selectIsDone,
selectIsMinimumStorageRequiredForAuditEntryLimitReached,
selectIsFaceVerificationConsent,
selectIsOVPViaDeepLink,
} from './scanSelectors';
describe('scanSelectors', () => {
const mockState: any = {
context: {
flowType: '',
openID4VPFlowType: '',
receiverInfo: null,
vcName: '',
selectedVc: null,
QrLoginRef: null,
showQuickShareSuccessBanner: false,
isOVPViaDeepLink: false,
showFaceCaptureSuccessBanner: false,
},
matches: jest.fn(() => false),
};
describe('selectFlowType', () => {
it('should return flowType from context', () => {
const state = {
...mockState,
context: {...mockState.context, flowType: 'simple_share'},
};
expect(selectFlowType(state)).toBe('simple_share');
});
});
describe('selectOpenID4VPFlowType', () => {
it('should return openID4VPFlowType from context', () => {
const state = {
...mockState,
context: {...mockState.context, openID4VPFlowType: 'ovp_flow'},
};
expect(selectOpenID4VPFlowType(state)).toBe('ovp_flow');
});
});
describe('selectReceiverInfo', () => {
it('should return receiverInfo from context', () => {
const receiverInfo = {name: 'Test', id: '123'};
const state = {
...mockState,
context: {...mockState.context, receiverInfo},
};
expect(selectReceiverInfo(state)).toBe(receiverInfo);
});
});
describe('selectVcName', () => {
it('should return vcName from context', () => {
const state = {
...mockState,
context: {...mockState.context, vcName: 'National ID'},
};
expect(selectVcName(state)).toBe('National ID');
});
});
describe('selectCredential', () => {
it('should return credential when verifiableCredential has credential property', () => {
const mockCredential = {id: 'cred123'};
const state = {
...mockState,
context: {
...mockState.context,
selectedVc: {
verifiableCredential: {credential: mockCredential},
},
},
};
const result = selectCredential(state);
expect(result).toEqual([mockCredential]);
});
it('should return verifiableCredential when no credential property', () => {
const mockVC = {id: 'vc456'};
const state = {
...mockState,
context: {
...mockState.context,
selectedVc: {
verifiableCredential: mockVC,
},
},
};
const result = selectCredential(state);
expect(result).toEqual([mockVC]);
});
});
describe('selectVerifiableCredentialData', () => {
it('should return formatted credential data with face from credentialSubject', () => {
const mockCredential = {
credentialSubject: {
face: '/9j/4AAQSkZJRgABAQAAAQ...',
gender: 'Male',
UIN: '123456789',
},
issuer: 'did:web:example.com',
issuanceDate: '2023-01-01T00:00:00Z',
};
const state = {
...mockState,
context: {
...mockState.context,
selectedVc: {
verifiableCredential: {credential: mockCredential},
vcMetadata: {},
credential: mockCredential,
format: 'ldp_vc',
},
},
};
const result = selectVerifiableCredentialData(state);
expect(result).toBeDefined();
expect(Array.isArray(result)).toBe(true);
expect(result[0]).toBeDefined();
expect(result[0].face).toBeDefined();
});
it('should use biometrics face when credentialSubject face is not available', () => {
const mockCredential = {
credentialSubject: {
gender: 'Male',
UIN: '123456789',
},
biometrics: {
face: '/9j/4AAQSkZJRgABAQBBBB...',
},
issuer: 'did:web:example.com',
issuanceDate: '2023-01-01T00:00:00Z',
};
const state = {
...mockState,
context: {
...mockState.context,
selectedVc: {
verifiableCredential: {credential: mockCredential},
vcMetadata: {},
credential: mockCredential,
format: 'ldp_vc',
},
},
};
const result = selectVerifiableCredentialData(state);
expect(result).toBeDefined();
expect(Array.isArray(result)).toBe(true);
expect(result[0].face).toBe('/9j/4AAQSkZJRgABAQBBBB...');
});
it('should handle credential without face data', () => {
const mockCredential = {
credentialSubject: {
gender: 'Male',
UIN: '123456789',
},
issuer: 'did:web:example.com',
issuanceDate: '2023-01-01T00:00:00Z',
};
const state = {
...mockState,
context: {
...mockState.context,
selectedVc: {
verifiableCredential: {credential: mockCredential},
vcMetadata: {},
credential: mockCredential,
format: 'ldp_vc',
},
},
};
const result = selectVerifiableCredentialData(state);
expect(result).toBeDefined();
expect(Array.isArray(result)).toBe(true);
expect(result[0].face).toBeUndefined();
});
});
describe('selectQrLoginRef', () => {
it('should return QrLoginRef from context', () => {
const ref = {current: 'test'};
const state = {
...mockState,
context: {...mockState.context, QrLoginRef: ref},
};
expect(selectQrLoginRef(state)).toBe(ref);
});
});
describe('selectIsScanning', () => {
it('should return true when in findingConnection state', () => {
const state = {
...mockState,
matches: jest.fn((s: string) => s === 'findingConnection'),
};
expect(selectIsScanning(state)).toBe(true);
});
it('should return false when not in findingConnection state', () => {
expect(selectIsScanning(mockState)).toBe(false);
});
});
describe('selectIsQuickShareDone', () => {
it('should return true when in loadVCS.navigatingToHome state', () => {
const state = {
...mockState,
matches: jest.fn((s: string) => s === 'loadVCS.navigatingToHome'),
};
expect(selectIsQuickShareDone(state)).toBe(true);
});
it('should return false when not in state', () => {
expect(selectIsQuickShareDone(mockState)).toBe(false);
});
});
describe('selectShowQuickShareSuccessBanner', () => {
it('should return showQuickShareSuccessBanner from context', () => {
const state = {
...mockState,
context: {...mockState.context, showQuickShareSuccessBanner: true},
};
expect(selectShowQuickShareSuccessBanner(state)).toBe(true);
});
});
describe('selectIsConnecting', () => {
it('should return true when in connecting.inProgress state', () => {
const state = {
...mockState,
matches: jest.fn((s: string) => s === 'connecting.inProgress'),
};
expect(selectIsConnecting(state)).toBe(true);
});
});
describe('selectIsConnectingTimeout', () => {
it('should return true when in connecting.timeout state', () => {
const state = {
...mockState,
matches: jest.fn((s: string) => s === 'connecting.timeout'),
};
expect(selectIsConnectingTimeout(state)).toBe(true);
});
});
describe('selectIsSelectingVc', () => {
it('should return true when in reviewing.selectingVc state', () => {
const state = {
...mockState,
matches: jest.fn((s: string) => s === 'reviewing.selectingVc'),
};
expect(selectIsSelectingVc(state)).toBe(true);
});
});
describe('selectIsSendingVc', () => {
it('should return true when in reviewing.sendingVc.inProgress state', () => {
const state = {
...mockState,
matches: jest.fn((s: string) => s === 'reviewing.sendingVc.inProgress'),
};
expect(selectIsSendingVc(state)).toBe(true);
});
});
describe('selectIsSendingVP', () => {
it('should return true when in startVPSharing.inProgress state', () => {
const state = {
...mockState,
matches: jest.fn((s: string) => s === 'startVPSharing.inProgress'),
};
expect(selectIsSendingVP(state)).toBe(true);
});
});
describe('selectIsSendingVPError', () => {
it('should return true when in startVPSharing.showError state', () => {
const state = {
...mockState,
matches: jest.fn((s: string) => s === 'startVPSharing.showError'),
};
expect(selectIsSendingVPError(state)).toBe(true);
});
});
describe('selectIsSendingVPSuccess', () => {
it('should return true when in startVPSharing.success state', () => {
const state = {
...mockState,
matches: jest.fn((s: string) => s === 'startVPSharing.success'),
};
expect(selectIsSendingVPSuccess(state)).toBe(true);
});
});
describe('selectIsFaceIdentityVerified', () => {
it('should return true when sendingVc and banner is true', () => {
const state = {
...mockState,
matches: jest.fn((s: string) => s === 'reviewing.sendingVc.inProgress'),
context: {...mockState.context, showFaceCaptureSuccessBanner: true},
};
expect(selectIsFaceIdentityVerified(state)).toBe(true);
});
it('should return false when not in sendingVc state', () => {
const state = {
...mockState,
context: {...mockState.context, showFaceCaptureSuccessBanner: true},
};
expect(selectIsFaceIdentityVerified(state)).toBe(false);
});
});
describe('selectIsSendingVcTimeout', () => {
it('should return true when in reviewing.sendingVc.timeout state', () => {
const state = {
...mockState,
matches: jest.fn((s: string) => s === 'reviewing.sendingVc.timeout'),
};
expect(selectIsSendingVcTimeout(state)).toBe(true);
});
});
describe('selectIsSendingVPTimeout', () => {
it('should return true when in startVPSharing.timeout state', () => {
const state = {
...mockState,
matches: jest.fn((s: string) => s === 'startVPSharing.timeout'),
};
expect(selectIsSendingVPTimeout(state)).toBe(true);
});
});
describe('selectIsSent', () => {
it('should return true when in reviewing.sendingVc.sent state', () => {
const state = {
...mockState,
matches: jest.fn((s: string) => s === 'reviewing.sendingVc.sent'),
};
expect(selectIsSent(state)).toBe(true);
});
});
describe('selectIsInvalid', () => {
it('should return true when in invalid state', () => {
const state = {
...mockState,
matches: jest.fn((s: string) => s === 'invalid'),
};
expect(selectIsInvalid(state)).toBe(true);
});
});
describe('selectIsLocationDenied', () => {
it('should return true when in checkingLocationState.denied state', () => {
const state = {
...mockState,
matches: jest.fn((s: string) => s === 'checkingLocationState.denied'),
};
expect(selectIsLocationDenied(state)).toBe(true);
});
});
describe('selectIsLocationDisabled', () => {
it('should return true when in checkingLocationState.disabled state', () => {
const state = {
...mockState,
matches: jest.fn((s: string) => s === 'checkingLocationState.disabled'),
};
expect(selectIsLocationDisabled(state)).toBe(true);
});
});
describe('selectIsShowQrLogin', () => {
it('should return true when in showQrLogin state', () => {
const state = {
...mockState,
matches: jest.fn((s: string) => s === 'showQrLogin'),
};
expect(selectIsShowQrLogin(state)).toBe(true);
});
});
describe('selectIsQrLoginDone', () => {
it('should return true when in showQrLogin.navigatingToHistory state', () => {
const state = {
...mockState,
matches: jest.fn(
(s: string) => s === 'showQrLogin.navigatingToHistory',
),
};
expect(selectIsQrLoginDone(state)).toBe(true);
});
});
describe('selectIsQrLoginDoneViaDeeplink', () => {
it('should return true when in showQrLogin.navigatingToHome state', () => {
const state = {
...mockState,
matches: jest.fn((s: string) => s === 'showQrLogin.navigatingToHome'),
};
expect(selectIsQrLoginDoneViaDeeplink(state)).toBe(true);
});
});
describe('selectIsQrLoginStoring', () => {
it('should return true when in showQrLogin.storing state', () => {
const state = {
...mockState,
matches: jest.fn((s: string) => s === 'showQrLogin.storing'),
};
expect(selectIsQrLoginStoring(state)).toBe(true);
});
});
describe('selectIsDone', () => {
it('should return true when in reviewing.disconnect state', () => {
const state = {
...mockState,
matches: jest.fn((s: string) => s === 'reviewing.disconnect'),
};
expect(selectIsDone(state)).toBe(true);
});
});
describe('selectIsMinimumStorageRequiredForAuditEntryLimitReached', () => {
it('should return true when in restrictSharingVc state', () => {
const state = {
...mockState,
matches: jest.fn((s: string) => s === 'restrictSharingVc'),
};
expect(
selectIsMinimumStorageRequiredForAuditEntryLimitReached(state),
).toBe(true);
});
});
describe('selectIsFaceVerificationConsent', () => {
it('should return true when in reviewing.faceVerificationConsent state', () => {
const state = {
...mockState,
matches: jest.fn(
(s: string) => s === 'reviewing.faceVerificationConsent',
),
};
expect(selectIsFaceVerificationConsent(state)).toBe(true);
});
});
describe('selectIsOVPViaDeepLink', () => {
it('should return isOVPViaDeepLink from context', () => {
const state = {
...mockState,
context: {...mockState.context, isOVPViaDeepLink: true},
};
expect(selectIsOVPViaDeepLink(state)).toBe(true);
});
});
});

View File

@@ -0,0 +1,350 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import {openID4VPModel} from './openID4VPModel';
import {KeyTypes} from '../../shared/cryptoutil/KeyTypes';
describe('openID4VPModel', () => {
describe('Model context', () => {
it('should initialize with default context values', () => {
const initialContext = openID4VPModel.initialContext;
expect(initialContext.urlEncodedAuthorizationRequest).toBe('');
expect(initialContext.checkedAll).toBe(false);
expect(initialContext.isShareWithSelfie).toBe(false);
expect(initialContext.showFaceAuthConsent).toBe(true);
expect(initialContext.purpose).toBe('');
expect(initialContext.error).toBe('');
expect(initialContext.publicKey).toBe('');
expect(initialContext.privateKey).toBe('');
expect(initialContext.keyType).toBe(KeyTypes.ED25519);
expect(initialContext.flowType).toBe('');
expect(initialContext.openID4VPRetryCount).toBe(0);
expect(initialContext.showFaceCaptureSuccessBanner).toBe(false);
expect(initialContext.isFaceVerificationRetryAttempt).toBe(false);
expect(initialContext.requestedClaims).toBe('');
expect(initialContext.showLoadingScreen).toBe(false);
expect(initialContext.isOVPViaDeepLink).toBe(false);
expect(initialContext.showTrustConsentModal).toBe(false);
});
it('should initialize with empty object contexts', () => {
const initialContext = openID4VPModel.initialContext;
expect(initialContext.authenticationResponse).toEqual({});
expect(initialContext.vcsMatchingAuthRequest).toEqual({});
expect(initialContext.selectedVCs).toEqual({});
expect(initialContext.selectedDisclosuresByVc).toEqual({});
expect(initialContext.miniViewSelectedVC).toEqual({});
});
it('should initialize with empty array contexts', () => {
const initialContext = openID4VPModel.initialContext;
expect(initialContext.trustedVerifiers).toEqual([]);
});
});
describe('Events', () => {
describe('AUTHENTICATE', () => {
it('should create AUTHENTICATE event with all parameters', () => {
const encodedAuthRequest = 'encoded_request_123';
const flowType = 'OpenID4VP';
const selectedVC = {id: 'vc123'};
const isOVPViaDeepLink = true;
const event = openID4VPModel.events.AUTHENTICATE(
encodedAuthRequest,
flowType,
selectedVC,
isOVPViaDeepLink,
);
expect(event.encodedAuthRequest).toBe(encodedAuthRequest);
expect(event.flowType).toBe(flowType);
expect(event.selectedVC).toEqual(selectedVC);
expect(event.isOVPViaDeepLink).toBe(isOVPViaDeepLink);
});
it('should create AUTHENTICATE event with false deeplink flag', () => {
const event = openID4VPModel.events.AUTHENTICATE(
'request',
'flow',
{},
false,
);
expect(event.isOVPViaDeepLink).toBe(false);
});
});
describe('DOWNLOADED_VCS', () => {
it('should create DOWNLOADED_VCS event with VCs array', () => {
const vcs: any[] = [
{id: 'vc1', credential: {}},
{id: 'vc2', credential: {}},
];
const event = openID4VPModel.events.DOWNLOADED_VCS(vcs);
expect(event.vcs).toEqual(vcs);
expect(event.vcs.length).toBe(2);
});
it('should create DOWNLOADED_VCS event with empty array', () => {
const event = openID4VPModel.events.DOWNLOADED_VCS([]);
expect(event.vcs).toEqual([]);
});
});
describe('SELECT_VC', () => {
it('should create SELECT_VC event with vcKey and inputDescriptorId', () => {
const vcKey = 'vc_key_123';
const inputDescriptorId = 'descriptor_456';
const event = openID4VPModel.events.SELECT_VC(vcKey, inputDescriptorId);
expect(event.vcKey).toBe(vcKey);
expect(event.inputDescriptorId).toBe(inputDescriptorId);
});
it('should create SELECT_VC event with null inputDescriptorId', () => {
const event = openID4VPModel.events.SELECT_VC('key', null);
expect(event.vcKey).toBe('key');
expect(event.inputDescriptorId).toBeNull();
});
});
describe('ACCEPT_REQUEST', () => {
it('should create ACCEPT_REQUEST event with selectedVCs and disclosures', () => {
const selectedVCs = {
descriptor1: [{id: 'vc1'}] as any[],
descriptor2: [{id: 'vc2'}] as any[],
};
const selectedDisclosuresByVc = {
vc1: ['claim1', 'claim2'],
vc2: ['claim3'],
};
const event = openID4VPModel.events.ACCEPT_REQUEST(
selectedVCs,
selectedDisclosuresByVc,
);
expect(event.selectedVCs).toEqual(selectedVCs);
expect(event.selectedDisclosuresByVc).toEqual(selectedDisclosuresByVc);
});
it('should create ACCEPT_REQUEST event with empty objects', () => {
const event = openID4VPModel.events.ACCEPT_REQUEST({}, {});
expect(event.selectedVCs).toEqual({});
expect(event.selectedDisclosuresByVc).toEqual({});
});
});
describe('VERIFIER_TRUST_CONSENT_GIVEN', () => {
it('should create VERIFIER_TRUST_CONSENT_GIVEN event', () => {
const event = openID4VPModel.events.VERIFIER_TRUST_CONSENT_GIVEN();
expect(event.type).toBe('VERIFIER_TRUST_CONSENT_GIVEN');
});
});
describe('VERIFY_AND_ACCEPT_REQUEST', () => {
it('should create VERIFY_AND_ACCEPT_REQUEST event with selectedVCs and disclosures', () => {
const selectedVCs = {descriptor1: [{id: 'vc1'}] as any[]};
const selectedDisclosuresByVc = {vc1: ['claim1']};
const event = openID4VPModel.events.VERIFY_AND_ACCEPT_REQUEST(
selectedVCs,
selectedDisclosuresByVc,
);
expect(event.selectedVCs).toEqual(selectedVCs);
expect(event.selectedDisclosuresByVc).toEqual(selectedDisclosuresByVc);
});
});
describe('CONFIRM', () => {
it('should create CONFIRM event', () => {
const event = openID4VPModel.events.CONFIRM();
expect(event.type).toBe('CONFIRM');
});
});
describe('CANCEL', () => {
it('should create CANCEL event', () => {
const event = openID4VPModel.events.CANCEL();
expect(event.type).toBe('CANCEL');
});
});
describe('FACE_VERIFICATION_CONSENT', () => {
it('should create FACE_VERIFICATION_CONSENT event with checked flag true', () => {
const event = openID4VPModel.events.FACE_VERIFICATION_CONSENT(true);
expect(event.isDoNotAskAgainChecked).toBe(true);
});
it('should create FACE_VERIFICATION_CONSENT event with checked flag false', () => {
const event = openID4VPModel.events.FACE_VERIFICATION_CONSENT(false);
expect(event.isDoNotAskAgainChecked).toBe(false);
});
});
describe('FACE_VALID', () => {
it('should create FACE_VALID event', () => {
const event = openID4VPModel.events.FACE_VALID();
expect(event.type).toBe('FACE_VALID');
});
});
describe('FACE_INVALID', () => {
it('should create FACE_INVALID event', () => {
const event = openID4VPModel.events.FACE_INVALID();
expect(event.type).toBe('FACE_INVALID');
});
});
describe('DISMISS', () => {
it('should create DISMISS event', () => {
const event = openID4VPModel.events.DISMISS();
expect(event.type).toBe('DISMISS');
});
});
describe('DISMISS_POPUP', () => {
it('should create DISMISS_POPUP event', () => {
const event = openID4VPModel.events.DISMISS_POPUP();
expect(event.type).toBe('DISMISS_POPUP');
});
});
describe('RETRY_VERIFICATION', () => {
it('should create RETRY_VERIFICATION event', () => {
const event = openID4VPModel.events.RETRY_VERIFICATION();
expect(event.type).toBe('RETRY_VERIFICATION');
});
});
describe('STORE_RESPONSE', () => {
it('should create STORE_RESPONSE event with response object', () => {
const response = {
status: 'success',
data: {id: '123'},
};
const event = openID4VPModel.events.STORE_RESPONSE(response);
expect(event.response).toEqual(response);
});
it('should create STORE_RESPONSE event with null response', () => {
const event = openID4VPModel.events.STORE_RESPONSE(null);
expect(event.response).toBeNull();
});
});
describe('GO_BACK', () => {
it('should create GO_BACK event', () => {
const event = openID4VPModel.events.GO_BACK();
expect(event.type).toBe('GO_BACK');
});
});
describe('CHECK_SELECTED_VC', () => {
it('should create CHECK_SELECTED_VC event', () => {
const event = openID4VPModel.events.CHECK_SELECTED_VC();
expect(event.type).toBe('CHECK_SELECTED_VC');
});
});
describe('SET_SELECTED_VC', () => {
it('should create SET_SELECTED_VC event', () => {
const event = openID4VPModel.events.SET_SELECTED_VC();
expect(event.type).toBe('SET_SELECTED_VC');
});
});
describe('CHECK_FOR_IMAGE', () => {
it('should create CHECK_FOR_IMAGE event', () => {
const event = openID4VPModel.events.CHECK_FOR_IMAGE();
expect(event.type).toBe('CHECK_FOR_IMAGE');
});
});
describe('RETRY', () => {
it('should create RETRY event', () => {
const event = openID4VPModel.events.RETRY();
expect(event.type).toBe('RETRY');
});
});
describe('RESET_RETRY_COUNT', () => {
it('should create RESET_RETRY_COUNT event', () => {
const event = openID4VPModel.events.RESET_RETRY_COUNT();
expect(event.type).toBe('RESET_RETRY_COUNT');
});
});
describe('RESET_ERROR', () => {
it('should create RESET_ERROR event', () => {
const event = openID4VPModel.events.RESET_ERROR();
expect(event.type).toBe('RESET_ERROR');
});
});
describe('CLOSE_BANNER', () => {
it('should create CLOSE_BANNER event', () => {
const event = openID4VPModel.events.CLOSE_BANNER();
expect(event.type).toBe('CLOSE_BANNER');
});
});
describe('LOG_ACTIVITY', () => {
it('should create LOG_ACTIVITY event with SHARED_SUCCESSFULLY logType', () => {
const event = openID4VPModel.events.LOG_ACTIVITY('SHARED_SUCCESSFULLY');
expect(event.logType).toBe('SHARED_SUCCESSFULLY');
});
it('should create LOG_ACTIVITY event with USER_DECLINED_CONSENT logType', () => {
const event = openID4VPModel.events.LOG_ACTIVITY(
'USER_DECLINED_CONSENT',
);
expect(event.logType).toBe('USER_DECLINED_CONSENT');
});
it('should create LOG_ACTIVITY event with TECHNICAL_ERROR logType', () => {
const event = openID4VPModel.events.LOG_ACTIVITY('TECHNICAL_ERROR');
expect(event.logType).toBe('TECHNICAL_ERROR');
});
it('should create LOG_ACTIVITY event with empty string logType', () => {
const event = openID4VPModel.events.LOG_ACTIVITY('');
expect(event.logType).toBe('');
});
});
});
});

View File

@@ -0,0 +1,637 @@
import {
selectIsGetVCsSatisfyingAuthRequest,
selectVCsMatchingAuthRequest,
selectSelectedVCs,
selectAreAllVCsChecked,
selectIsGetVPSharingConsent,
selectIsFaceVerificationConsent,
selectIsVerifyingIdentity,
selectIsInvalidIdentity,
selectIsSharingVP,
selectIsShowLoadingScreen,
selectCredentials,
selectVerifiableCredentialsData,
selectPurpose,
selectShowConfirmationPopup,
selectIsSelectingVcs,
selectIsError,
selectOpenID4VPRetryCount,
selectIsOVPViaDeeplink,
selectIsFaceVerifiedInVPSharing,
selectVerifierNameInVPSharing,
selectRequestedClaimsByVerifier,
selectshowTrustConsentModal,
selectVerifierNameInTrustModal,
selectVerifierLogoInTrustModal,
} from './openID4VPSelectors';
describe('openID4VPSelectors', () => {
describe('selectIsGetVCsSatisfyingAuthRequest', () => {
it('should return true when in getVCsSatisfyingAuthRequest state', () => {
const state = {
matches: jest.fn(() => true),
context: {},
} as any;
expect(selectIsGetVCsSatisfyingAuthRequest(state)).toBe(true);
expect(state.matches).toHaveBeenCalledWith('getVCsSatisfyingAuthRequest');
});
});
describe('selectVCsMatchingAuthRequest', () => {
it('should return vcsMatchingAuthRequest from context', () => {
const mockVCs = [{id: 'vc1'}, {id: 'vc2'}];
const state = {
context: {
vcsMatchingAuthRequest: mockVCs,
},
} as any;
expect(selectVCsMatchingAuthRequest(state)).toEqual(mockVCs);
});
});
describe('selectSelectedVCs', () => {
it('should return selectedVCs from context', () => {
const mockSelectedVCs = {vc1: {data: 'test'}};
const state = {
context: {
selectedVCs: mockSelectedVCs,
},
} as any;
expect(selectSelectedVCs(state)).toEqual(mockSelectedVCs);
});
});
describe('selectAreAllVCsChecked', () => {
it('should return checkedAll from context', () => {
const state = {
context: {
checkedAll: true,
},
} as any;
expect(selectAreAllVCsChecked(state)).toBe(true);
});
});
describe('selectIsGetVPSharingConsent', () => {
it('should return true when in getConsentForVPSharing state', () => {
const state = {
matches: jest.fn(() => true),
context: {},
} as any;
expect(selectIsGetVPSharingConsent(state)).toBe(true);
expect(state.matches).toHaveBeenCalledWith('getConsentForVPSharing');
});
});
describe('selectIsFaceVerificationConsent', () => {
it('should return true when in faceVerificationConsent state', () => {
const state = {
matches: jest.fn(() => true),
context: {},
} as any;
expect(selectIsFaceVerificationConsent(state)).toBe(true);
expect(state.matches).toHaveBeenCalledWith('faceVerificationConsent');
});
});
describe('selectIsVerifyingIdentity', () => {
it('should return true when in verifyingIdentity state', () => {
const state = {
matches: jest.fn(() => true),
context: {},
} as any;
expect(selectIsVerifyingIdentity(state)).toBe(true);
expect(state.matches).toHaveBeenCalledWith('verifyingIdentity');
});
});
describe('selectIsInvalidIdentity', () => {
it('should return true when in invalidIdentity state', () => {
const state = {
matches: jest.fn(() => true),
context: {},
} as any;
expect(selectIsInvalidIdentity(state)).toBe(true);
expect(state.matches).toHaveBeenCalledWith('invalidIdentity');
});
});
describe('selectIsSharingVP', () => {
it('should return true when in sendingVP state', () => {
const state = {
matches: jest.fn(() => true),
context: {},
} as any;
expect(selectIsSharingVP(state)).toBe(true);
expect(state.matches).toHaveBeenCalledWith('sendingVP');
});
});
describe('selectIsShowLoadingScreen', () => {
it('should return showLoadingScreen from context', () => {
const state = {
context: {
showLoadingScreen: true,
},
} as any;
expect(selectIsShowLoadingScreen(state)).toBe(true);
});
});
describe('selectCredentials', () => {
it('should return empty array when selectedVCs is empty', () => {
const state = {
context: {
selectedVCs: {},
},
} as any;
expect(selectCredentials(state)).toEqual([]);
});
it('should process and return credentials from selectedVCs', () => {
const mockCredential = {data: 'test'};
const state = {
context: {
selectedVCs: {
key1: {
inner1: [
{
verifiableCredential: {
credential: mockCredential,
},
},
],
},
},
},
} as any;
const result = selectCredentials(state);
expect(result).toEqual([mockCredential]);
});
it('should handle credentials without nested credential property', () => {
const mockCredential = {data: 'test'};
const state = {
context: {
selectedVCs: {
key1: {
inner1: [
{
verifiableCredential: mockCredential,
},
],
},
},
},
} as any;
const result = selectCredentials(state);
expect(result).toEqual([mockCredential]);
});
});
describe('selectVerifiableCredentialsData', () => {
it('should return empty array when no selectedVCs', () => {
const state = {
context: {
selectedVCs: {},
},
} as any;
expect(selectVerifiableCredentialsData(state)).toEqual([]);
});
it('should process and return verifiable credentials data', () => {
const state = {
context: {
selectedVCs: {
key1: {
inner1: [
{
vcMetadata: {
issuer: 'TestIssuer',
},
verifiableCredential: {
issuerLogo: 'logo.png',
wellKnown: 'wellknown',
credentialTypes: ['type1'],
credential: {
credentialSubject: {
face: 'faceData',
},
},
},
},
],
},
},
},
} as any;
const result = selectVerifiableCredentialsData(state);
expect(result).toHaveLength(1);
expect(result[0].issuer).toBe('TestIssuer');
expect(result[0].face).toBe('faceData');
});
});
describe('selectPurpose', () => {
it('should return purpose from context', () => {
const state = {
context: {
purpose: 'verification',
},
} as any;
expect(selectPurpose(state)).toBe('verification');
});
});
describe('selectShowConfirmationPopup', () => {
it('should return true when in showConfirmationPopup state', () => {
const state = {
matches: jest.fn(() => true),
context: {},
} as any;
expect(selectShowConfirmationPopup(state)).toBe(true);
expect(state.matches).toHaveBeenCalledWith('showConfirmationPopup');
});
});
describe('selectIsSelectingVcs', () => {
it('should return true when in selectingVCs state', () => {
const state = {
matches: jest.fn(() => true),
context: {},
} as any;
expect(selectIsSelectingVcs(state)).toBe(true);
expect(state.matches).toHaveBeenCalledWith('selectingVCs');
});
});
describe('selectIsError', () => {
it('should return error from context', () => {
const mockError = new Error('Test error');
const state = {
context: {
error: mockError,
},
} as any;
expect(selectIsError(state)).toBe(mockError);
});
});
describe('selectOpenID4VPRetryCount', () => {
it('should return openID4VPRetryCount from context', () => {
const state = {
context: {
openID4VPRetryCount: 3,
},
} as any;
expect(selectOpenID4VPRetryCount(state)).toBe(3);
});
});
describe('selectIsOVPViaDeeplink', () => {
it('should return isOVPViaDeepLink from context', () => {
const state = {
context: {
isOVPViaDeepLink: true,
},
} as any;
expect(selectIsOVPViaDeeplink(state)).toBe(true);
});
});
describe('selectIsFaceVerifiedInVPSharing', () => {
it('should return true when in sendingVP state and banner shown', () => {
const state = {
matches: jest.fn(() => true),
context: {
showFaceCaptureSuccessBanner: true,
},
} as any;
expect(selectIsFaceVerifiedInVPSharing(state)).toBe(true);
expect(state.matches).toHaveBeenCalledWith('sendingVP');
});
it('should return false when not in sendingVP state', () => {
const state = {
matches: jest.fn(() => false),
context: {
showFaceCaptureSuccessBanner: true,
},
} as any;
expect(selectIsFaceVerifiedInVPSharing(state)).toBe(false);
});
});
describe('selectVerifierNameInVPSharing', () => {
it('should return client_name from client_metadata', () => {
const state = {
context: {
authenticationResponse: {
client_metadata: {
client_name: 'TestVerifier',
},
},
},
} as any;
expect(selectVerifierNameInVPSharing(state)).toBe('TestVerifier');
});
it('should return client_id when client_name not available', () => {
const state = {
context: {
authenticationResponse: {
client_id: 'verifier123',
},
},
} as any;
expect(selectVerifierNameInVPSharing(state)).toBe('verifier123');
});
});
describe('selectRequestedClaimsByVerifier', () => {
it('should return requestedClaims from context', () => {
const mockClaims = ['name', 'age'];
const state = {
context: {
requestedClaims: mockClaims,
},
} as any;
expect(selectRequestedClaimsByVerifier(state)).toEqual(mockClaims);
});
});
describe('selectshowTrustConsentModal', () => {
it('should return showTrustConsentModal from context', () => {
const state = {
context: {
showTrustConsentModal: true,
},
} as any;
expect(selectshowTrustConsentModal(state)).toBe(true);
});
});
describe('selectVerifierNameInTrustModal', () => {
it('should return client_name from client_metadata', () => {
const state = {
context: {
authenticationResponse: {
client_metadata: {
client_name: 'TrustedVerifier',
},
},
},
} as any;
expect(selectVerifierNameInTrustModal(state)).toBe('TrustedVerifier');
});
});
describe('selectVerifierLogoInTrustModal', () => {
it('should return logo_uri from client_metadata', () => {
const state = {
context: {
authenticationResponse: {
client_metadata: {
logo_uri: 'https://example.com/logo.png',
},
},
},
} as any;
expect(selectVerifierLogoInTrustModal(state)).toBe(
'https://example.com/logo.png',
);
});
});
describe('selectCredentials', () => {
it('should return array of credentials from selectedVCs', () => {
const mockState: any = {
context: {
selectedVCs: {
type1: {
vc1: {
verifiableCredential: {
credential: {id: 'cred-1', name: 'John'},
},
},
},
type2: {
vc2: {
verifiableCredential: {id: 'cred-2', name: 'Jane'},
},
},
},
},
};
const result: any = selectCredentials(mockState);
expect(Array.isArray(result)).toBe(true);
expect(result.length).toBe(2);
});
it('should handle verifiableCredential without credential property', () => {
const mockState: any = {
context: {
selectedVCs: {
type1: {
vc1: {
verifiableCredential: {id: 'direct-vc', name: 'Direct'},
},
},
},
},
};
const result: any = selectCredentials(mockState);
expect(result.length).toBe(1);
});
it('should return undefined when no credentials selected', () => {
const mockState: any = {
context: {
selectedVCs: {},
},
};
const result: any = selectCredentials(mockState);
expect(Array.isArray(result) ? result.length : result).toBe(0);
});
it('should flatten nested credential structures', () => {
const mockState: any = {
context: {
selectedVCs: {
type1: {
vc1: {verifiableCredential: {credential: {id: '1'}}},
vc2: {verifiableCredential: {credential: {id: '2'}}},
},
type2: {
vc3: {verifiableCredential: {credential: {id: '3'}}},
},
},
},
};
const result: any = selectCredentials(mockState);
expect(result.length).toBe(3);
});
});
describe('selectVerifiableCredentialsData', () => {
it('should return array of formatted verifiable credential data', () => {
const mockState: any = {
context: {
selectedVCs: {
type1: {
vc1: {
vcMetadata: {
id: 'vc-001',
issuer: 'Test Issuer',
},
verifiableCredential: {
issuerLogo: 'https://example.com/logo.png',
wellKnown: 'https://example.com/.well-known',
credentialTypes: ['VerifiableCredential', 'NationalID'],
credential: {
credentialSubject: {
face: 'base64-image-data',
},
},
},
},
},
},
},
};
const result = selectVerifiableCredentialsData(mockState);
expect(Array.isArray(result)).toBe(true);
expect(result.length).toBe(1);
expect(result[0].issuer).toBe('Test Issuer');
expect(result[0].issuerLogo).toBe('https://example.com/logo.png');
expect(result[0].wellKnown).toBe('https://example.com/.well-known');
});
it('should use getMosipLogo when issuerLogo is not available', () => {
const mockState: any = {
context: {
selectedVCs: {
type1: {
vc1: {
vcMetadata: {
id: 'vc-002',
issuer: 'Mosip',
},
verifiableCredential: {
credential: {
credentialSubject: {},
},
},
},
},
},
},
};
const result = selectVerifiableCredentialsData(mockState);
expect(result[0].issuerLogo).toBeDefined();
});
it('should handle face from credential.biometrics.face', () => {
const mockState: any = {
context: {
selectedVCs: {
type1: {
vc1: {
vcMetadata: {
id: 'vc-003',
issuer: 'Issuer',
},
credential: {
biometrics: {
face: 'biometric-face-data',
},
},
verifiableCredential: {},
},
},
},
},
};
const result = selectVerifiableCredentialsData(mockState);
expect(result[0].face).toBe('biometric-face-data');
});
it('should handle empty selectedVCs', () => {
const mockState: any = {
context: {
selectedVCs: {},
},
};
const result = selectVerifiableCredentialsData(mockState);
expect(result).toEqual([]);
});
it('should handle multiple VCs from different types', () => {
const mockState: any = {
context: {
selectedVCs: {
type1: {
vc1: {
vcMetadata: {id: '1', issuer: 'Issuer1'},
verifiableCredential: {},
},
},
type2: {
vc2: {
vcMetadata: {id: '2', issuer: 'Issuer2'},
verifiableCredential: {},
},
vc3: {
vcMetadata: {id: '3', issuer: 'Issuer3'},
verifiableCredential: {},
},
},
},
},
};
const result = selectVerifiableCredentialsData(mockState);
expect(result.length).toBe(3);
expect(result[0].issuer).toBe('Issuer1');
expect(result[1].issuer).toBe('Issuer2');
expect(result[2].issuer).toBe('Issuer3');
});
});
});

View File

@@ -0,0 +1,139 @@
import {
BOTTOM_TAB_ROUTES,
SCAN_ROUTES,
REQUEST_ROUTES,
SETTINGS_ROUTES,
AUTH_ROUTES,
} from './routesConstants';
describe('routesConstants', () => {
describe('BOTTOM_TAB_ROUTES', () => {
it('should be defined', () => {
expect(BOTTOM_TAB_ROUTES).toBeDefined();
});
it('should have home route', () => {
expect(BOTTOM_TAB_ROUTES.home).toBe('home');
});
it('should have share route', () => {
expect(BOTTOM_TAB_ROUTES.share).toBe('share');
});
it('should have history route', () => {
expect(BOTTOM_TAB_ROUTES.history).toBe('history');
});
it('should have settings route', () => {
expect(BOTTOM_TAB_ROUTES.settings).toBe('settings');
});
it('should have exactly 4 routes', () => {
expect(Object.keys(BOTTOM_TAB_ROUTES)).toHaveLength(4);
});
});
describe('SCAN_ROUTES', () => {
it('should be defined', () => {
expect(SCAN_ROUTES).toBeDefined();
});
it('should have ScanScreen route', () => {
expect(SCAN_ROUTES.ScanScreen).toBe('ScanScreen');
});
it('should have SendVcScreen route', () => {
expect(SCAN_ROUTES.SendVcScreen).toBe('SendVcScreen');
});
it('should have SendVPScreen route', () => {
expect(SCAN_ROUTES.SendVPScreen).toBe('SendVPScreen');
});
it('should have exactly 3 routes', () => {
expect(Object.keys(SCAN_ROUTES)).toHaveLength(3);
});
});
describe('REQUEST_ROUTES', () => {
it('should be defined', () => {
expect(REQUEST_ROUTES).toBeDefined();
});
it('should have Request route', () => {
expect(REQUEST_ROUTES.Request).toBe('Request');
});
it('should have RequestScreen route', () => {
expect(REQUEST_ROUTES.RequestScreen).toBe('RequestScreen');
});
it('should have ReceiveVcScreen route', () => {
expect(REQUEST_ROUTES.ReceiveVcScreen).toBe('ReceiveVcScreen');
});
it('should have exactly 3 routes', () => {
expect(Object.keys(REQUEST_ROUTES)).toHaveLength(3);
});
});
describe('SETTINGS_ROUTES', () => {
it('should be defined', () => {
expect(SETTINGS_ROUTES).toBeDefined();
});
it('should have KeyManagement route', () => {
expect(SETTINGS_ROUTES.KeyManagement).toBe('KeyManagement');
});
it('should have exactly 1 route', () => {
expect(Object.keys(SETTINGS_ROUTES)).toHaveLength(1);
});
});
describe('AUTH_ROUTES', () => {
it('should be defined', () => {
expect(AUTH_ROUTES).toBeDefined();
});
it('should have AuthView route', () => {
expect(AUTH_ROUTES.AuthView).toBe('AuthView');
});
it('should have exactly 1 route', () => {
expect(Object.keys(AUTH_ROUTES)).toHaveLength(1);
});
});
describe('Route constants structure', () => {
it('all BOTTOM_TAB_ROUTES values should be strings', () => {
Object.values(BOTTOM_TAB_ROUTES).forEach(route => {
expect(typeof route).toBe('string');
});
});
it('all SCAN_ROUTES values should be strings', () => {
Object.values(SCAN_ROUTES).forEach(route => {
expect(typeof route).toBe('string');
});
});
it('all REQUEST_ROUTES values should be strings', () => {
Object.values(REQUEST_ROUTES).forEach(route => {
expect(typeof route).toBe('string');
});
});
it('all SETTINGS_ROUTES values should be strings', () => {
Object.values(SETTINGS_ROUTES).forEach(route => {
expect(typeof route).toBe('string');
});
});
it('all AUTH_ROUTES values should be strings', () => {
Object.values(AUTH_ROUTES).forEach(route => {
expect(typeof route).toBe('string');
});
});
});
});

405
shared/Utils.test.ts Normal file
View File

@@ -0,0 +1,405 @@
import {
getVCsOrderedByPinStatus,
VCShareFlowType,
VCItemContainerFlowType,
CameraPosition,
isMosipVC,
parseJSON,
isNetworkError,
UUID,
formatTextWithGivenLimit,
DEEPLINK_FLOWS,
base64ToByteArray,
createCacheObject,
isCacheExpired,
getVerifierKey,
} from './Utils';
import {VCMetadata} from './VCMetadata';
describe('getVCsOrderedByPinStatus', () => {
it('should be defined', () => {
expect(getVCsOrderedByPinStatus).toBeDefined();
});
it('should return pinned VCs first', () => {
const vcMetadatas = [
new VCMetadata({id: '1', isPinned: false}),
new VCMetadata({id: '2', isPinned: true}),
new VCMetadata({id: '3', isPinned: false}),
new VCMetadata({id: '4', isPinned: true}),
];
const result = getVCsOrderedByPinStatus(vcMetadatas);
expect(result[0].isPinned).toBe(true);
expect(result[1].isPinned).toBe(true);
expect(result[2].isPinned).toBe(false);
expect(result[3].isPinned).toBe(false);
});
it('should handle empty array', () => {
const result = getVCsOrderedByPinStatus([]);
expect(result).toEqual([]);
});
it('should handle all pinned VCs', () => {
const vcMetadatas = [
new VCMetadata({id: '1', isPinned: true}),
new VCMetadata({id: '2', isPinned: true}),
];
const result = getVCsOrderedByPinStatus(vcMetadatas);
expect(result.every(vc => vc.isPinned)).toBe(true);
});
it('should handle all unpinned VCs', () => {
const vcMetadatas = [
new VCMetadata({id: '1', isPinned: false}),
new VCMetadata({id: '2', isPinned: false}),
];
const result = getVCsOrderedByPinStatus(vcMetadatas);
expect(result.every(vc => !vc.isPinned)).toBe(true);
});
});
describe('VCShareFlowType enum', () => {
it('should have SIMPLE_SHARE defined', () => {
expect(VCShareFlowType.SIMPLE_SHARE).toBe('simple share');
});
it('should have MINI_VIEW_SHARE defined', () => {
expect(VCShareFlowType.MINI_VIEW_SHARE).toBe('mini view share');
});
it('should have MINI_VIEW_SHARE_WITH_SELFIE defined', () => {
expect(VCShareFlowType.MINI_VIEW_SHARE_WITH_SELFIE).toBe(
'mini view share with selfie',
);
});
it('should have MINI_VIEW_QR_LOGIN defined', () => {
expect(VCShareFlowType.MINI_VIEW_QR_LOGIN).toBe('mini view qr login');
});
it('should have OPENID4VP defined', () => {
expect(VCShareFlowType.OPENID4VP).toBe('OpenID4VP');
});
it('should have MINI_VIEW_SHARE_OPENID4VP defined', () => {
expect(VCShareFlowType.MINI_VIEW_SHARE_OPENID4VP).toBe(
'OpenID4VP share from mini view',
);
});
it('should have MINI_VIEW_SHARE_WITH_SELFIE_OPENID4VP defined', () => {
expect(VCShareFlowType.MINI_VIEW_SHARE_WITH_SELFIE_OPENID4VP).toBe(
'OpenID4VP share with selfie from mini view',
);
});
});
describe('VCItemContainerFlowType enum', () => {
it('should have QR_LOGIN defined', () => {
expect(VCItemContainerFlowType.QR_LOGIN).toBe('qr login');
});
it('should have VC_SHARE defined', () => {
expect(VCItemContainerFlowType.VC_SHARE).toBe('vc share');
});
it('should have VP_SHARE defined', () => {
expect(VCItemContainerFlowType.VP_SHARE).toBe('vp share');
});
});
describe('CameraPosition enum', () => {
it('should have FRONT defined', () => {
expect(CameraPosition.FRONT).toBe('front');
});
it('should have BACK defined', () => {
expect(CameraPosition.BACK).toBe('back');
});
});
describe('isMosipVC', () => {
it('should be defined', () => {
expect(isMosipVC).toBeDefined();
});
it('should return true for Mosip issuer', () => {
const result = isMosipVC('Mosip');
expect(result).toBe(true);
});
it('should return true for MosipOtp issuer', () => {
const result = isMosipVC('MosipOtp');
expect(result).toBe(true);
});
it('should return false for other issuers', () => {
expect(isMosipVC('SomeOtherIssuer')).toBe(false);
expect(isMosipVC('')).toBe(false);
expect(isMosipVC('mosip')).toBe(false);
});
});
describe('parseJSON', () => {
it('should be defined', () => {
expect(parseJSON).toBeDefined();
});
it('should parse valid JSON string', () => {
const jsonStr = '{"key": "value"}';
const result = parseJSON(jsonStr);
expect(result).toEqual({key: 'value'});
});
it('should handle object input', () => {
const obj = {key: 'value'};
const result = parseJSON(obj);
expect(result).toEqual({key: 'value'});
});
it('should handle invalid JSON gracefully', () => {
const invalidJson = '{invalid json}';
const result = parseJSON(invalidJson);
expect(result).toBeDefined();
});
it('should handle nested objects', () => {
const jsonStr = '{"key": {"nested": "value"}}';
const result = parseJSON(jsonStr);
expect(result.key.nested).toBe('value');
});
});
describe('isNetworkError', () => {
it('should be defined', () => {
expect(isNetworkError).toBeDefined();
});
it('should return true for network request failed error', () => {
const error = 'Network request failed';
expect(isNetworkError(error)).toBe(true);
});
it('should return false for other errors', () => {
expect(isNetworkError('Some other error')).toBe(false);
expect(isNetworkError('')).toBe(false);
});
it('should handle partial matches', () => {
const error = 'Error: Network request failed - timeout';
expect(isNetworkError(error)).toBe(true);
});
});
describe('UUID', () => {
it('should be defined', () => {
expect(UUID).toBeDefined();
});
it('should generate a valid UUID', () => {
const uuid = UUID.generate();
expect(uuid).toBeDefined();
expect(typeof uuid).toBe('string');
expect(uuid.length).toBeGreaterThan(0);
});
it('should generate unique UUIDs', () => {
const uuid1 = UUID.generate();
const uuid2 = UUID.generate();
expect(uuid1).not.toBe(uuid2);
});
it('should match UUID format', () => {
const uuid = UUID.generate();
const uuidRegex =
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
expect(uuidRegex.test(uuid)).toBe(true);
});
});
describe('formatTextWithGivenLimit', () => {
it('should be defined', () => {
expect(formatTextWithGivenLimit).toBeDefined();
});
it('should truncate text longer than limit', () => {
const text = 'This is a very long text';
const result = formatTextWithGivenLimit(text, 10);
expect(result).toBe('This is a ...');
});
it('should return text as is if shorter than limit', () => {
const text = 'Short text';
const result = formatTextWithGivenLimit(text, 15);
expect(result).toBe('Short text');
});
it('should use default limit of 15 if not provided', () => {
const text = 'This is a longer text than 15 characters';
const result = formatTextWithGivenLimit(text);
expect(result).toBe('This is a longe...');
});
it('should handle empty string', () => {
const result = formatTextWithGivenLimit('', 10);
expect(result).toBe('');
});
it('should handle exact limit length', () => {
const text = 'Exactly 10';
const result = formatTextWithGivenLimit(text, 10);
expect(result).toBe('Exactly 10');
});
});
describe('DEEPLINK_FLOWS enum', () => {
it('should have QR_LOGIN defined', () => {
expect(DEEPLINK_FLOWS.QR_LOGIN).toBe('qrLoginFlow');
});
it('should have OVP defined', () => {
expect(DEEPLINK_FLOWS.OVP).toBe('ovpFlow');
});
});
describe('base64ToByteArray', () => {
it('should be defined', () => {
expect(base64ToByteArray).toBeDefined();
});
it('should convert base64 string to byte array', () => {
const base64 = 'SGVsbG8gV29ybGQ='; // "Hello World"
const result = base64ToByteArray(base64);
expect(result).toBeInstanceOf(Uint8Array);
expect(result.length).toBeGreaterThan(0);
});
it('should handle base64url encoding', () => {
const base64url = 'SGVsbG8gV29ybGQ'; // without padding
const result = base64ToByteArray(base64url);
expect(result).toBeInstanceOf(Uint8Array);
});
it('should throw error for invalid base64', () => {
expect(() => {
base64ToByteArray('!!!invalid base64!!!');
}).toThrow();
});
it('should handle strings with whitespace', () => {
const base64 = ' SGVsbG8gV29ybGQ= ';
const result = base64ToByteArray(base64);
expect(result).toBeInstanceOf(Uint8Array);
});
it('should handle URL-safe base64 characters', () => {
const base64 = 'SGVsbG8tV29ybGQ_'; // with - and _
const result = base64ToByteArray(base64);
expect(result).toBeInstanceOf(Uint8Array);
});
it('should handle empty string', () => {
const result = base64ToByteArray('');
expect(result).toBeInstanceOf(Uint8Array);
expect(result.length).toBe(0);
});
});
describe('createCacheObject', () => {
it('should create cache object with response and timestamp', () => {
const response = {data: 'test data'};
const result = createCacheObject(response);
expect(result).toHaveProperty('response');
expect(result).toHaveProperty('cachedTime');
expect(result.response).toBe(response);
expect(typeof result.cachedTime).toBe('number');
});
it('should use current timestamp', () => {
const before = Date.now();
const result = createCacheObject({});
const after = Date.now();
expect(result.cachedTime).toBeGreaterThanOrEqual(before);
expect(result.cachedTime).toBeLessThanOrEqual(after);
});
it('should handle null response', () => {
const result = createCacheObject(null);
expect(result.response).toBeNull();
expect(result.cachedTime).toBeDefined();
});
it('should handle complex objects', () => {
const complexResponse = {
data: [1, 2, 3],
metadata: {key: 'value'},
nested: {deep: {value: true}},
};
const result = createCacheObject(complexResponse);
expect(result.response).toBe(complexResponse);
});
});
describe('isCacheExpired', () => {
it('should return false for recent timestamp', () => {
const recentTimestamp = Date.now() - 1000; // 1 second ago
expect(isCacheExpired(recentTimestamp)).toBe(false);
});
it('should return true for old timestamp', () => {
const oldTimestamp = Date.now() - (60 * 60 * 1000 + 1000); // Over 1 hour ago
expect(isCacheExpired(oldTimestamp)).toBe(true);
});
it('should return false for current timestamp', () => {
const currentTimestamp = Date.now();
expect(isCacheExpired(currentTimestamp)).toBe(false);
});
it('should handle edge case at exact TTL boundary', () => {
const boundaryTimestamp = Date.now() - 60 * 60 * 1000; // Exactly 1 hour ago
const result = isCacheExpired(boundaryTimestamp);
expect(typeof result).toBe('boolean');
});
});
describe('getVerifierKey', () => {
it('should create verifier key with prefix', () => {
const verifier = 'example.com';
const result = getVerifierKey(verifier);
expect(result).toBe('trusted_verifier_example.com');
});
it('should handle empty string', () => {
const result = getVerifierKey('');
expect(result).toBe('trusted_verifier_');
});
it('should preserve verifier name exactly', () => {
const verifier = 'TestVerifier123';
const result = getVerifierKey(verifier);
expect(result).toBe('trusted_verifier_TestVerifier123');
});
it('should handle special characters', () => {
const verifier = 'verifier-with-dashes_and_underscores';
const result = getVerifierKey(verifier);
expect(result).toBe(
'trusted_verifier_verifier-with-dashes_and_underscores',
);
});
});
describe('canonicalize', () => {
it('should be defined', async () => {
const {canonicalize} = await import('./Utils');
expect(canonicalize).toBeDefined();
});
});

37
shared/VCFormat.test.ts Normal file
View File

@@ -0,0 +1,37 @@
import {VCFormat} from './VCFormat';
describe('VCFormat', () => {
it('should have ldp_vc format', () => {
expect(VCFormat.ldp_vc).toBe('ldp_vc');
});
it('should have mso_mdoc format', () => {
expect(VCFormat.mso_mdoc).toBe('mso_mdoc');
});
it('should have vc_sd_jwt format', () => {
expect(VCFormat.vc_sd_jwt).toBe('vc+sd-jwt');
});
it('should have dc_sd_jwt format', () => {
expect(VCFormat.dc_sd_jwt).toBe('dc+sd-jwt');
});
it('should have exactly 4 formats', () => {
const formatCount = Object.keys(VCFormat).length;
expect(formatCount).toBe(4);
});
it('should allow access via enum key', () => {
expect(VCFormat['ldp_vc']).toBe('ldp_vc');
expect(VCFormat['mso_mdoc']).toBe('mso_mdoc');
expect(VCFormat['vc_sd_jwt']).toBe('vc+sd-jwt');
expect(VCFormat['dc_sd_jwt']).toBe('dc+sd-jwt');
});
it('should have all unique values', () => {
const values = Object.values(VCFormat);
const uniqueValues = new Set(values);
expect(uniqueValues.size).toBe(values.length);
});
});

499
shared/VCMetadata.test.ts Normal file
View File

@@ -0,0 +1,499 @@
import {VCMetadata, parseMetadatas, getVCMetadata} from './VCMetadata';
import {VCFormat} from './VCFormat';
import {UUID} from './Utils';
describe('VCMetadata', () => {
describe('constructor', () => {
it('should create instance with default values', () => {
const metadata = new VCMetadata();
expect(metadata.idType).toBe('');
expect(metadata.requestId).toBe('');
expect(metadata.isPinned).toBe(false);
expect(metadata.id).toBe('');
expect(metadata.issuer).toBe('');
expect(metadata.protocol).toBe('');
expect(metadata.timestamp).toBe('');
expect(metadata.isVerified).toBe(false);
expect(metadata.mosipIndividualId).toBe('');
expect(metadata.format).toBe('');
expect(metadata.isExpired).toBe(false);
});
it('should create instance with provided values', () => {
const metadata = new VCMetadata({
idType: 'UIN',
requestId: 'req123',
isPinned: true,
id: 'id123',
issuer: 'TestIssuer',
protocol: 'OpenId4VCI',
timestamp: '2024-01-01',
isVerified: true,
mosipIndividualId: 'mosip123',
format: 'ldp_vc',
downloadKeyType: 'ED25519',
isExpired: false,
credentialType: 'NationalID',
issuerHost: 'https://test.com',
});
expect(metadata.idType).toBe('UIN');
expect(metadata.requestId).toBe('req123');
expect(metadata.isPinned).toBe(true);
expect(metadata.id).toBe('id123');
expect(metadata.issuer).toBe('TestIssuer');
expect(metadata.protocol).toBe('OpenId4VCI');
expect(metadata.timestamp).toBe('2024-01-01');
expect(metadata.isVerified).toBe(true);
expect(metadata.mosipIndividualId).toBe('mosip123');
expect(metadata.format).toBe('ldp_vc');
expect(metadata.downloadKeyType).toBe('ED25519');
expect(metadata.isExpired).toBe(false);
expect(metadata.credentialType).toBe('NationalID');
expect(metadata.issuerHost).toBe('https://test.com');
});
});
describe('fromVC', () => {
it('should create VCMetadata from VC object', () => {
const vc = {
idType: 'VID',
requestId: 'req456',
id: 'vc123',
issuer: 'Issuer1',
format: VCFormat.ldp_vc,
};
const metadata = VCMetadata.fromVC(vc);
expect(metadata.idType).toBe('VID');
expect(metadata.requestId).toBe('req456');
expect(metadata.id).toBe('vc123');
expect(metadata.issuer).toBe('Issuer1');
expect(metadata.format).toBe(VCFormat.ldp_vc);
});
it('should use default format if not provided', () => {
const vc = {id: 'vc123'};
const metadata = VCMetadata.fromVC(vc);
expect(metadata.format).toBe(VCFormat.ldp_vc);
});
it('should handle isPinned default value', () => {
const vc = {id: 'vc123'};
const metadata = VCMetadata.fromVC(vc);
expect(metadata.isPinned).toBe(false);
});
});
describe('fromVcMetadataString', () => {
it('should parse JSON string to VCMetadata', () => {
const jsonStr = JSON.stringify({
id: 'vc123',
issuer: 'TestIssuer',
format: 'ldp_vc',
});
const metadata = VCMetadata.fromVcMetadataString(jsonStr);
expect(metadata.id).toBe('vc123');
expect(metadata.issuer).toBe('TestIssuer');
expect(metadata.format).toBe('ldp_vc');
});
it('should handle object input', () => {
const obj = {
id: 'vc456',
issuer: 'AnotherIssuer',
};
const metadata = VCMetadata.fromVcMetadataString(obj);
expect(metadata.id).toBe('vc456');
expect(metadata.issuer).toBe('AnotherIssuer');
});
it('should return empty VCMetadata on parse error', () => {
const invalidJson = '{invalid json}';
const metadata = VCMetadata.fromVcMetadataString(invalidJson);
expect(metadata).toBeInstanceOf(VCMetadata);
expect(metadata.id).toBe('');
});
});
describe('isVCKey', () => {
it('should return true for valid VC key', () => {
expect(VCMetadata.isVCKey('VC_1234567890_abc123')).toBe(true);
expect(VCMetadata.isVCKey('VC_timestamp_id')).toBe(true);
});
it('should return false for invalid VC key', () => {
expect(VCMetadata.isVCKey('INVALID_KEY')).toBe(false);
expect(VCMetadata.isVCKey('VC')).toBe(false);
expect(VCMetadata.isVCKey('')).toBe(false);
expect(VCMetadata.isVCKey('VC_')).toBe(false);
});
it('should handle keys with special characters properly', () => {
expect(VCMetadata.isVCKey('VC_123_abc-def')).toBe(true);
expect(VCMetadata.isVCKey('VC_123_abc_def')).toBe(true);
});
});
describe('isFromOpenId4VCI', () => {
it('should return true when protocol is OpenId4VCI', () => {
const metadata = new VCMetadata({protocol: 'OpenId4VCI'});
expect(metadata.isFromOpenId4VCI()).toBe(true);
});
it('should return false when protocol is not OpenId4VCI', () => {
const metadata = new VCMetadata({protocol: 'OtherProtocol'});
expect(metadata.isFromOpenId4VCI()).toBe(false);
});
it('should return false when protocol is empty', () => {
const metadata = new VCMetadata();
expect(metadata.isFromOpenId4VCI()).toBe(false);
});
});
describe('getVcKey', () => {
it('should generate VC key with timestamp', () => {
const metadata = new VCMetadata({
timestamp: '1234567890',
id: 'abc123',
});
expect(metadata.getVcKey()).toBe('VC_1234567890_abc123');
});
it('should generate VC key without timestamp', () => {
const metadata = new VCMetadata({
timestamp: '',
id: 'xyz789',
});
expect(metadata.getVcKey()).toBe('VC_xyz789');
});
it('should match the VC key regex pattern', () => {
const metadata = new VCMetadata({
timestamp: '1234567890',
id: 'test-id_123',
});
const key = metadata.getVcKey();
expect(VCMetadata.isVCKey(key)).toBe(true);
});
});
describe('equals', () => {
it('should return true for equal VCMetadata instances', () => {
const metadata1 = new VCMetadata({
timestamp: '1234567890',
id: 'abc123',
});
const metadata2 = new VCMetadata({
timestamp: '1234567890',
id: 'abc123',
});
expect(metadata1.equals(metadata2)).toBe(true);
});
it('should return false for different VCMetadata instances', () => {
const metadata1 = new VCMetadata({
timestamp: '1234567890',
id: 'abc123',
});
const metadata2 = new VCMetadata({
timestamp: '0987654321',
id: 'xyz789',
});
expect(metadata1.equals(metadata2)).toBe(false);
});
it('should return true when comparing instance with itself', () => {
const metadata = new VCMetadata({
timestamp: '1234567890',
id: 'abc123',
});
expect(metadata.equals(metadata)).toBe(true);
});
});
describe('vcKeyRegExp', () => {
it('should be defined as a RegExp', () => {
expect(VCMetadata.vcKeyRegExp).toBeInstanceOf(RegExp);
});
});
});
describe('parseMetadatas', () => {
it('should be defined', () => {
expect(parseMetadatas).toBeDefined();
});
it('should parse array of metadata objects', () => {
const metadataObjects = [
{id: 'vc1', issuer: 'Issuer1'},
{id: 'vc2', issuer: 'Issuer2'},
{id: 'vc3', issuer: 'Issuer3'},
];
const result = parseMetadatas(metadataObjects);
expect(result).toHaveLength(3);
expect(result[0]).toBeInstanceOf(VCMetadata);
expect(result[0].id).toBe('vc1');
expect(result[1].id).toBe('vc2');
expect(result[2].id).toBe('vc3');
});
it('should handle empty array', () => {
const result = parseMetadatas([]);
expect(result).toEqual([]);
});
it('should create VCMetadata instances for each object', () => {
const metadataObjects = [
{id: 'test1', format: 'ldp_vc', isPinned: true},
{id: 'test2', format: 'mso_mdoc', isPinned: false},
];
const result = parseMetadatas(metadataObjects);
expect(result[0].id).toBe('test1');
expect(result[0].format).toBe('ldp_vc');
expect(result[0].isPinned).toBe(true);
expect(result[1].id).toBe('test2');
expect(result[1].format).toBe('mso_mdoc');
expect(result[1].isPinned).toBe(false);
});
});
describe('getVCMetadata', () => {
beforeEach(() => {
jest.spyOn(UUID, 'generate').mockReturnValue('test-uuid-12345');
});
afterEach(() => {
jest.restoreAllMocks();
});
it('should create VCMetadata with generated credential ID', () => {
const mockContext: any = {
selectedIssuer: {
credential_issuer_host: 'https://issuer.example.com',
issuer_id: 'TestIssuer',
protocol: 'OpenId4VCI',
},
timestamp: '1234567890',
vcMetadata: {
isVerified: false,
isExpired: false,
},
verifiableCredential: null,
credentialWrapper: {
format: VCFormat.ldp_vc,
},
selectedCredentialType: null,
};
const result = getVCMetadata(mockContext, 'ED25519');
expect(result.requestId).toContain('test-uuid-12345');
expect(result.requestId).toContain('issuer');
expect(result.issuer).toBe('TestIssuer');
expect(result.protocol).toBe('OpenId4VCI');
expect(result.timestamp).toBe('1234567890');
expect(result.downloadKeyType).toBe('ED25519');
expect(result.format).toBe(VCFormat.ldp_vc);
});
it('should handle credential_issuer when credential_issuer_host is not available', () => {
const mockContext: any = {
selectedIssuer: {
credential_issuer: 'https://backup.example.com',
issuer_id: 'BackupIssuer',
protocol: 'OpenId4VCI',
},
timestamp: '',
vcMetadata: {},
verifiableCredential: null,
credentialWrapper: {
format: VCFormat.mso_mdoc,
},
selectedCredentialType: null,
};
const result = getVCMetadata(mockContext, 'RSA');
expect(result.issuer).toBe('BackupIssuer');
expect(result.issuerHost).toBe('https://backup.example.com');
expect(result.format).toBe(VCFormat.mso_mdoc);
});
it('should use credential_issuer as fallback for issuer_id', () => {
const mockContext: any = {
selectedIssuer: {
credential_issuer_host: 'https://issuer.test.com',
credential_issuer: 'FallbackIssuer',
protocol: 'OIDC',
},
timestamp: '9876543210',
vcMetadata: {
isVerified: true,
isExpired: false,
},
verifiableCredential: null,
credentialWrapper: {
format: VCFormat.vc_sd_jwt,
},
selectedCredentialType: null,
};
const result = getVCMetadata(mockContext, 'ECDSA');
expect(result.issuer).toBe('FallbackIssuer');
expect(result.isVerified).toBe(true);
expect(result.downloadKeyType).toBe('ECDSA');
});
it('should extract issuer name from URL', () => {
const mockContext: any = {
selectedIssuer: {
credential_issuer_host: 'https://subdomain.example.org',
issuer_id: 'ExampleOrg',
protocol: 'OpenId4VCI',
},
timestamp: '',
vcMetadata: {},
verifiableCredential: null,
credentialWrapper: {format: VCFormat.ldp_vc},
selectedCredentialType: null,
};
const result = getVCMetadata(mockContext, 'ED25519');
expect(result.requestId).toContain('subdomain');
});
it('should handle invalid URL gracefully', () => {
const mockContext: any = {
selectedIssuer: {
credential_issuer_host: 'not-a-valid-url',
issuer_id: 'TestIssuer',
protocol: 'OpenId4VCI',
},
timestamp: '',
vcMetadata: {},
verifiableCredential: null,
credentialWrapper: {format: VCFormat.ldp_vc},
selectedCredentialType: null,
};
const result = getVCMetadata(mockContext, 'ED25519');
expect(result.requestId).toContain('not-a-valid-url');
expect(result.issuerHost).toBe('not-a-valid-url');
});
it('should handle Mosip VC with UIN', () => {
const mockContext: any = {
selectedIssuer: {
credential_issuer_host: 'https://mosip.example.com',
issuer_id: 'Mosip',
protocol: 'OpenId4VCI',
},
timestamp: '',
vcMetadata: {},
verifiableCredential: {
credential: {
credentialSubject: {
UIN: '1234567890',
},
},
},
credentialWrapper: {format: VCFormat.ldp_vc},
selectedCredentialType: null,
};
const result = getVCMetadata(mockContext, 'ED25519');
expect(result.mosipIndividualId).toBe('1234567890');
});
it('should handle Mosip VC with VID', () => {
const mockContext: any = {
selectedIssuer: {
credential_issuer_host: 'https://mosip.example.com',
issuer_id: 'Mosip',
protocol: 'OpenId4VCI',
},
timestamp: '',
vcMetadata: {},
verifiableCredential: {
credential: {
credentialSubject: {
VID: '9876543210',
},
},
},
credentialWrapper: {format: VCFormat.ldp_vc},
selectedCredentialType: null,
};
const result = getVCMetadata(mockContext, 'ED25519');
expect(result.mosipIndividualId).toBe('9876543210');
});
it('should set credential type when provided', () => {
const mockContext: any = {
selectedIssuer: {
credential_issuer_host: 'https://issuer.example.com',
issuer_id: 'TestIssuer',
protocol: 'OpenId4VCI',
},
timestamp: '1234567890',
vcMetadata: {},
verifiableCredential: null,
credentialWrapper: {format: VCFormat.mso_mdoc},
selectedCredentialType: 'org.iso.18013.5.1.mDL',
};
const result = getVCMetadata(mockContext, 'RSA');
expect(result.credentialType).toBeDefined();
expect(result.format).toBe(VCFormat.mso_mdoc);
});
it('should handle different key types', () => {
const mockContext: any = {
selectedIssuer: {
credential_issuer_host: 'https://issuer.example.com',
issuer_id: 'TestIssuer',
protocol: 'OpenId4VCI',
},
timestamp: '',
vcMetadata: {},
verifiableCredential: null,
credentialWrapper: {format: VCFormat.vc_sd_jwt},
selectedCredentialType: null,
};
const resultRSA = getVCMetadata(mockContext, 'RS256');
expect(resultRSA.downloadKeyType).toBe('RS256');
const resultEC = getVCMetadata(mockContext, 'ES256');
expect(resultEC.downloadKeyType).toBe('ES256');
});
});

216
shared/api.test.ts Normal file
View File

@@ -0,0 +1,216 @@
import {API_URLS} from './api';
describe('API_URLS configuration', () => {
describe('trustedVerifiersList', () => {
it('should have GET method', () => {
expect(API_URLS.trustedVerifiersList.method).toBe('GET');
});
it('should build correct URL', () => {
expect(API_URLS.trustedVerifiersList.buildURL()).toBe(
'/v1/mimoto/verifiers',
);
});
});
describe('issuersList', () => {
it('should have GET method', () => {
expect(API_URLS.issuersList.method).toBe('GET');
});
it('should build correct URL', () => {
expect(API_URLS.issuersList.buildURL()).toBe('/v1/mimoto/issuers');
});
});
describe('issuerConfig', () => {
it('should have GET method', () => {
expect(API_URLS.issuerConfig.method).toBe('GET');
});
it('should build correct URL with issuer id', () => {
expect(API_URLS.issuerConfig.buildURL('test-issuer')).toBe(
'/v1/mimoto/issuers/test-issuer',
);
});
});
describe('issuerWellknownConfig', () => {
it('should have GET method', () => {
expect(API_URLS.issuerWellknownConfig.method).toBe('GET');
});
it('should build correct URL with credential issuer', () => {
expect(
API_URLS.issuerWellknownConfig.buildURL('https://example.com'),
).toBe('https://example.com/.well-known/openid-credential-issuer');
});
});
describe('authorizationServerMetadataConfig', () => {
it('should have GET method', () => {
expect(API_URLS.authorizationServerMetadataConfig.method).toBe('GET');
});
it('should build correct URL with authorization server URL', () => {
expect(
API_URLS.authorizationServerMetadataConfig.buildURL(
'https://auth.example.com',
),
).toBe('https://auth.example.com/.well-known/oauth-authorization-server');
});
});
describe('allProperties', () => {
it('should have GET method', () => {
expect(API_URLS.allProperties.method).toBe('GET');
});
it('should build correct URL', () => {
expect(API_URLS.allProperties.buildURL()).toBe(
'/v1/mimoto/allProperties',
);
});
});
describe('getIndividualId', () => {
it('should have POST method', () => {
expect(API_URLS.getIndividualId.method).toBe('POST');
});
it('should build correct URL', () => {
expect(API_URLS.getIndividualId.buildURL()).toBe(
'/v1/mimoto/aid/get-individual-id',
);
});
});
describe('reqIndividualOTP', () => {
it('should have POST method', () => {
expect(API_URLS.reqIndividualOTP.method).toBe('POST');
});
it('should build correct URL', () => {
expect(API_URLS.reqIndividualOTP.buildURL()).toBe(
'/v1/mimoto/req/individualId/otp',
);
});
});
describe('walletBinding', () => {
it('should have POST method', () => {
expect(API_URLS.walletBinding.method).toBe('POST');
});
it('should build correct URL', () => {
expect(API_URLS.walletBinding.buildURL()).toBe(
'/v1/mimoto/wallet-binding',
);
});
});
describe('bindingOtp', () => {
it('should have POST method', () => {
expect(API_URLS.bindingOtp.method).toBe('POST');
});
it('should build correct URL', () => {
expect(API_URLS.bindingOtp.buildURL()).toBe('/v1/mimoto/binding-otp');
});
});
describe('requestOtp', () => {
it('should have POST method', () => {
expect(API_URLS.requestOtp.method).toBe('POST');
});
it('should build correct URL', () => {
expect(API_URLS.requestOtp.buildURL()).toBe('/v1/mimoto/req/otp');
});
});
describe('credentialRequest', () => {
it('should have POST method', () => {
expect(API_URLS.credentialRequest.method).toBe('POST');
});
it('should build correct URL', () => {
expect(API_URLS.credentialRequest.buildURL()).toBe(
'/v1/mimoto/credentialshare/request',
);
});
});
describe('credentialStatus', () => {
it('should have GET method', () => {
expect(API_URLS.credentialStatus.method).toBe('GET');
});
it('should build correct URL with id', () => {
expect(API_URLS.credentialStatus.buildURL('request-123')).toBe(
'/v1/mimoto/credentialshare/request/status/request-123',
);
});
});
describe('credentialDownload', () => {
it('should have POST method', () => {
expect(API_URLS.credentialDownload.method).toBe('POST');
});
it('should build correct URL', () => {
expect(API_URLS.credentialDownload.buildURL()).toBe(
'/v1/mimoto/credentialshare/download',
);
});
});
describe('linkTransaction', () => {
it('should have POST method', () => {
expect(API_URLS.linkTransaction.method).toBe('POST');
});
it('should build correct URL', () => {
expect(API_URLS.linkTransaction.buildURL()).toBe(
'/v1/esignet/linked-authorization/v2/link-transaction',
);
});
});
describe('authenticate', () => {
it('should have POST method', () => {
expect(API_URLS.authenticate.method).toBe('POST');
});
it('should build correct URL', () => {
expect(API_URLS.authenticate.buildURL()).toBe(
'/v1/esignet/linked-authorization/v2/authenticate',
);
});
});
describe('sendConsent', () => {
it('should have POST method', () => {
expect(API_URLS.sendConsent.method).toBe('POST');
});
it('should build correct URL', () => {
expect(API_URLS.sendConsent.buildURL()).toBe(
'/v1/esignet/linked-authorization/v2/consent',
);
});
});
describe('googleAccountProfileInfo', () => {
it('should have GET method', () => {
expect(API_URLS.googleAccountProfileInfo.method).toBe('GET');
});
it('should build correct URL with access token', () => {
const accessToken = 'test-token-123';
expect(API_URLS.googleAccountProfileInfo.buildURL(accessToken)).toBe(
`https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=${accessToken}`,
);
});
});
});

View File

@@ -1,4 +1,3 @@
import {useState} from 'react';
import testIDProps, {
bytesToMB,
faceMatchConfig,
@@ -12,8 +11,20 @@ import testIDProps, {
logState,
removeWhiteSpace,
sleep,
getRandomInt,
getMosipIdentifier,
isTranslationKeyFound,
getAccountType,
BYTES_IN_MEGABYTE,
} from './commonUtil';
import {argon2iConfig} from './constants';
import {
argon2iConfig,
GOOGLE_DRIVE_NAME,
ICLOUD_DRIVE_NAME,
GMAIL,
APPLE,
} from './constants';
import {CredentialSubject} from '../machines/VerifiableCredential/VCMetaMachine/vc.d';
describe('hashData', () => {
it('should expose a function', () => {
@@ -74,6 +85,27 @@ describe('removeWhiteSpace', () => {
const response = removeWhiteSpace('React Native Unit Testing');
expect(response).toBe('ReactNativeUnitTesting');
});
it('should handle empty string', () => {
expect(removeWhiteSpace('')).toBe('');
});
it('should handle string with only spaces', () => {
expect(removeWhiteSpace(' ')).toBe('');
});
it('should handle string with tabs and newlines', () => {
expect(removeWhiteSpace('Hello\tWorld\n')).toBe('HelloWorld');
});
it('should handle string with multiple types of whitespace', () => {
expect(removeWhiteSpace(' Test \t String \n ')).toBe('TestString');
});
it('should remove all whitespace from string', () => {
const result = removeWhiteSpace('Hello World Test');
expect(result).toBe('HelloWorldTest');
});
});
describe('logState', () => {
@@ -97,17 +129,27 @@ describe('getMaskedText', () => {
const maskedTxt = getMaskedText(id);
expect(maskedTxt).toBe('******7890');
});
});
describe('faceMatchConfig', () => {
it('should expose a function', () => {
expect(faceMatchConfig).toBeDefined();
it('should handle exactly 4 characters', () => {
expect(getMaskedText('1234')).toBe('1234');
});
// it('faceMatchConfig should return expected output', () => {
// // const retValue = faceMatchConfig(resp);
// expect(false).toBeTruthy();
// });
it('should handle long strings', () => {
const longString = '12345678901234567890';
const masked = getMaskedText(longString);
expect(masked.endsWith('7890')).toBe(true);
expect(masked.length).toBe(longString.length);
});
it('should handle short strings', () => {
const result = getMaskedText('ABCDEF');
expect(result).toBe('**CDEF');
});
it('should handle exactly 4 characters (ABCD)', () => {
const result = getMaskedText('ABCD');
expect(result).toBe('ABCD');
});
});
describe('getBackupFileName', () => {
@@ -116,26 +158,19 @@ describe('getBackupFileName', () => {
});
});
describe('bytesToMB', () => {
it('bytesToMB returns a string', () => {
expect(bytesToMB(0)).toBe('0');
});
it('10^6 bytes is 1MB', () => {
expect(bytesToMB(1e6)).toBe('1.000');
});
});
describe('getDriveName', () => {
it('should expose a function', () => {
expect(getDriveName).toBeDefined();
});
it('getDriveName should return Google Drive on Android', () => {
expect(getDriveName()).toBe('Google Drive');
it('should return a string', () => {
const result = getDriveName();
expect(typeof result).toBe('string');
});
it('getDriveName should return Google Drive on Android', () => {
expect(getDriveName()).toBe('Google Drive');
it('should return Google Drive for Android or iCloud for iOS', () => {
const result = getDriveName();
expect([GOOGLE_DRIVE_NAME, ICLOUD_DRIVE_NAME]).toContain(result);
});
});
@@ -149,6 +184,19 @@ describe('sleep : The promise resolves after a certain time', () => {
const promise = sleep(time);
expect(promise).toBeInstanceOf(Promise);
});
it('should delay for specified milliseconds', async () => {
const start = Date.now();
await sleep(100);
const end = Date.now();
const elapsed = end - start;
expect(elapsed).toBeGreaterThanOrEqual(90); // Allow small margin
});
it('should resolve after timeout', async () => {
const promise = sleep(50);
await expect(promise).resolves.toBeUndefined();
});
});
describe('getScreenHeight', () => {
@@ -160,4 +208,240 @@ describe('getScreenHeight', () => {
const height = getScreenHeight();
expect(typeof height).toBe('object');
});
it('should return a value', () => {
const height = getScreenHeight();
expect(height).toBeDefined();
});
});
describe('getRandomInt', () => {
it('should expose a function', () => {
expect(getRandomInt).toBeDefined();
});
it('should return a number within the specified range', () => {
const min = 1;
const max = 10;
const result = getRandomInt(min, max);
expect(result).toBeGreaterThanOrEqual(min);
expect(result).toBeLessThanOrEqual(max);
expect(Number.isInteger(result)).toBe(true);
});
it('should return min when min and max are equal', () => {
const value = 5;
const result = getRandomInt(value, value);
expect(result).toBe(value);
});
it('should handle negative ranges', () => {
const result = getRandomInt(-10, -1);
expect(result).toBeGreaterThanOrEqual(-10);
expect(result).toBeLessThanOrEqual(-1);
});
it('should handle larger ranges', () => {
const result = getRandomInt(100, 200);
expect(result).toBeGreaterThanOrEqual(100);
expect(result).toBeLessThanOrEqual(200);
});
});
describe('getMosipIdentifier', () => {
it('should expose a function', () => {
expect(getMosipIdentifier).toBeDefined();
});
it('should return UIN when UIN is present', () => {
const credentialSubject = {
UIN: '123456789',
VID: '987654321',
} as Partial<CredentialSubject>;
const result = getMosipIdentifier(credentialSubject as CredentialSubject);
expect(result).toBe('123456789');
});
it('should return VID when UIN is not present', () => {
const credentialSubject = {VID: '987654321'} as Partial<CredentialSubject>;
const result = getMosipIdentifier(credentialSubject as CredentialSubject);
expect(result).toBe('987654321');
});
it('should return undefined when neither UIN nor VID is present', () => {
const credentialSubject = {} as Partial<CredentialSubject>;
const result = getMosipIdentifier(credentialSubject as CredentialSubject);
expect(result).toBeUndefined();
});
it('should prioritize UIN over VID', () => {
const credSubject = {
UIN: '1111111111',
VID: '2222222222',
} as CredentialSubject;
expect(getMosipIdentifier(credSubject)).toBe('1111111111');
});
});
describe('isTranslationKeyFound', () => {
it('should expose a function', () => {
expect(isTranslationKeyFound).toBeDefined();
});
it('should return true when translation key is found', () => {
const mockT = jest.fn(() => 'Translated text');
const result = isTranslationKeyFound('someKey', mockT);
expect(result).toBe(true);
});
it('should return false when translation key is not found', () => {
const mockT = jest.fn((key: string) => key);
const result = isTranslationKeyFound('someKey', mockT);
expect(result).toBe(false);
});
it('should return true when key is translated', () => {
const mockT = () => 'Translated value';
expect(isTranslationKeyFound('any.key', mockT)).toBe(true);
});
it('should return false when translation key not found', () => {
const mockT = (key: string) => key; // returns same key
expect(isTranslationKeyFound('some.unknown.key', mockT)).toBe(false);
});
it('should return true for errors.notFound key when translation is found', () => {
const mockT = (key: string) => {
if (key === 'errors.notFound') return 'Error Not Found';
return key;
};
expect(isTranslationKeyFound('errors.notFound', mockT)).toBe(true);
});
});
describe('getAccountType', () => {
it('should expose a function', () => {
expect(getAccountType).toBeDefined();
});
it('should return a string', () => {
const result = getAccountType();
expect(typeof result).toBe('string');
});
it('should return Gmail for Android or Apple for iOS', () => {
const result = getAccountType();
expect([GMAIL, APPLE]).toContain(result);
});
});
describe('faceMatchConfig', () => {
it('should expose a function', () => {
expect(faceMatchConfig).toBeDefined();
});
it('should return a valid configuration object', () => {
const config = faceMatchConfig();
expect(config).toBeDefined();
expect(config.withFace).toBeDefined();
expect(config.withFace.encoder).toBeDefined();
expect(config.withFace.matcher).toBeDefined();
expect(config.withFace.encoder.tfModel).toBeDefined();
expect(config.withFace.matcher.threshold).toBe(1);
});
it('should return config with correct structure', () => {
const config = faceMatchConfig();
expect(config).toHaveProperty('withFace');
expect(config.withFace).toHaveProperty('encoder');
expect(config.withFace).toHaveProperty('matcher');
expect(config.withFace.encoder.tfModel).toHaveProperty('path');
expect(config.withFace.encoder.tfModel).toHaveProperty('modelChecksum');
expect(config.withFace.matcher.threshold).toBe(1);
});
});
describe('BYTES_IN_MEGABYTE', () => {
it('should be defined', () => {
expect(BYTES_IN_MEGABYTE).toBeDefined();
});
it('should equal 1,000,000', () => {
expect(BYTES_IN_MEGABYTE).toBe(1000000);
});
it('should be 1000 * 1000', () => {
expect(BYTES_IN_MEGABYTE).toBe(1000 * 1000);
});
it('should be a number', () => {
expect(typeof BYTES_IN_MEGABYTE).toBe('number');
});
it('should be positive', () => {
expect(BYTES_IN_MEGABYTE).toBeGreaterThan(0);
});
});
describe('bytesToMB', () => {
it('bytesToMB returns a string', () => {
expect(bytesToMB(0)).toBe('0');
});
it('10^6 bytes is 1MB', () => {
expect(bytesToMB(1e6)).toBe('1.000');
});
it('should return "0" for negative bytes', () => {
expect(bytesToMB(-100)).toBe('0');
});
it('should convert 1,000,000 bytes to "1.000" MB', () => {
expect(bytesToMB(1000000)).toBe('1.000');
});
it('should convert 2,500,000 bytes to "2.500" MB', () => {
expect(bytesToMB(2500000)).toBe('2.500');
});
it('should convert 500,000 bytes to "0.500" MB', () => {
expect(bytesToMB(500000)).toBe('0.500');
});
it('should handle large byte values', () => {
expect(bytesToMB(10000000)).toBe('10.000');
});
it('should handle small byte values', () => {
expect(bytesToMB(1000)).toBe('0.001');
});
it('should return three decimal places', () => {
const result = bytesToMB(1234567);
expect(result).toMatch(/^\d+\.\d{3}$/);
});
it('should handle fractional megabytes', () => {
expect(bytesToMB(1234567)).toBe('1.235');
});
it('should handle very small values', () => {
expect(bytesToMB(100)).toBe('0.000');
});
it('should handle exactly one byte', () => {
expect(bytesToMB(1)).toBe('0.000');
});
it('should convert bytes to megabytes', () => {
const bytes = BYTES_IN_MEGABYTE * 5; // 5 MB
const result = bytesToMB(bytes);
expect(result).toBe('5.000');
});
it('should handle fractional megabytes with BYTES_IN_MEGABYTE constant', () => {
const bytes = BYTES_IN_MEGABYTE * 2.5;
const result = bytesToMB(bytes);
expect(result).toBe('2.500');
});
});

View File

@@ -0,0 +1,37 @@
import {KeyTypes} from './KeyTypes';
describe('KeyTypes', () => {
it('should have RS256 key type', () => {
expect(KeyTypes.RS256).toBe('RS256');
});
it('should have ES256 key type', () => {
expect(KeyTypes.ES256).toBe('ES256');
});
it('should have ES256K key type', () => {
expect(KeyTypes.ES256K).toBe('ES256K');
});
it('should have ED25519 key type', () => {
expect(KeyTypes.ED25519).toBe('Ed25519');
});
it('should have exactly 4 key types', () => {
const keyTypeCount = Object.keys(KeyTypes).length;
expect(keyTypeCount).toBe(4);
});
it('should allow access via enum key', () => {
expect(KeyTypes['RS256']).toBe('RS256');
expect(KeyTypes['ES256']).toBe('ES256');
expect(KeyTypes['ES256K']).toBe('ES256K');
expect(KeyTypes['ED25519']).toBe('Ed25519');
});
it('should have all unique values', () => {
const values = Object.values(KeyTypes);
const uniqueValues = new Set(values);
expect(uniqueValues.size).toBe(values.length);
});
});

View File

@@ -0,0 +1,31 @@
import {BiometricCancellationError} from './BiometricCancellationError';
describe('BiometricCancellationError', () => {
it('should create an error instance with the correct message', () => {
const errorMessage = 'User cancelled biometric authentication';
const error = new BiometricCancellationError(errorMessage);
expect(error).toBeInstanceOf(Error);
expect(error).toBeInstanceOf(BiometricCancellationError);
expect(error.message).toBe(errorMessage);
});
it('should have the correct error name', () => {
const error = new BiometricCancellationError('Test error');
expect(error.name).toBe('BiometricCancellationError');
});
it('should maintain the error stack trace', () => {
const error = new BiometricCancellationError('Stack trace test');
expect(error.stack).toBeDefined();
});
it('should handle empty message', () => {
const error = new BiometricCancellationError('');
expect(error.message).toBe('');
expect(error.name).toBe('BiometricCancellationError');
});
});

Some files were not shown because too many files have changed in this diff Show More