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

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');
});
});

View File

@@ -0,0 +1,38 @@
import {UnsupportedVcFormat} from './UnsupportedVCFormat';
describe('UnsupportedVcFormat', () => {
it('should create an error instance with the correct format message', () => {
const format = 'jwt_vc_json';
const error = new UnsupportedVcFormat(format);
expect(error).toBeInstanceOf(Error);
expect(error).toBeInstanceOf(UnsupportedVcFormat);
expect(error.message).toBe(format);
});
it('should have the correct error name', () => {
const error = new UnsupportedVcFormat('ldp_vc');
expect(error.name).toBe('UnsupportedVcFormat');
});
it('should maintain the error stack trace', () => {
const error = new UnsupportedVcFormat('custom_format');
expect(error.stack).toBeDefined();
});
it('should handle empty format string', () => {
const error = new UnsupportedVcFormat('');
expect(error.message).toBe('');
expect(error.name).toBe('UnsupportedVcFormat');
});
it('should handle complex format strings', () => {
const format = 'application/vc+sd-jwt';
const error = new UnsupportedVcFormat(format);
expect(error.message).toBe(format);
});
});

73
shared/javascript.test.ts Normal file
View File

@@ -0,0 +1,73 @@
import {groupBy} from './javascript';
describe('javascript utils', () => {
describe('groupBy', () => {
it('should group elements based on predicate', () => {
const array = [1, 2, 3, 4, 5, 6];
const predicate = (num: number) => num % 2 === 0;
const [trueElements, falseElements] = groupBy(array, predicate);
expect(trueElements).toEqual([2, 4, 6]);
expect(falseElements).toEqual([1, 3, 5]);
});
it('should return empty arrays for empty input', () => {
const array: number[] = [];
const predicate = (num: number) => num > 0;
const [trueElements, falseElements] = groupBy(array, predicate);
expect(trueElements).toEqual([]);
expect(falseElements).toEqual([]);
});
it('should put all elements in true group when predicate always returns true', () => {
const array = ['a', 'b', 'c'];
const predicate = () => true;
const [trueElements, falseElements] = groupBy(array, predicate);
expect(trueElements).toEqual(['a', 'b', 'c']);
expect(falseElements).toEqual([]);
});
it('should put all elements in false group when predicate always returns false', () => {
const array = ['a', 'b', 'c'];
const predicate = () => false;
const [trueElements, falseElements] = groupBy(array, predicate);
expect(trueElements).toEqual([]);
expect(falseElements).toEqual(['a', 'b', 'c']);
});
it('should handle complex objects', () => {
const array = [
{name: 'John', age: 30},
{name: 'Jane', age: 25},
{name: 'Bob', age: 35},
];
const predicate = (person: {name: string; age: number}) =>
person.age >= 30;
const [trueElements, falseElements] = groupBy(array, predicate);
expect(trueElements).toEqual([
{name: 'John', age: 30},
{name: 'Bob', age: 35},
]);
expect(falseElements).toEqual([{name: 'Jane', age: 25}]);
});
it('should handle undefined array', () => {
const array = undefined as any;
const predicate = (num: number) => num > 0;
const [trueElements, falseElements] = groupBy(array, predicate);
expect(trueElements).toEqual([]);
expect(falseElements).toEqual([]);
});
});
});

View File

@@ -0,0 +1,407 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
Protocols,
Issuers,
isActivationNeeded,
ACTIVATION_NEEDED,
Issuers_Key_Ref,
getDisplayObjectForCurrentLanguage,
removeBottomSectionFields,
getMatchingCredentialIssuerMetadata,
selectCredentialRequestKey,
updateCredentialInformation,
} from './Utils';
import {VCFormat} from '../VCFormat';
// Mock VCProcessor
jest.mock('../../components/VC/common/VCProcessor', () => ({
VCProcessor: {
processForRendering: jest.fn().mockResolvedValue({
processedData: 'mocked-processed-credential',
}),
},
}));
describe('openId4VCI Utils', () => {
describe('Protocols', () => {
it('should have OpenId4VCI protocol defined', () => {
expect(Protocols.OpenId4VCI).toBe('OpenId4VCI');
});
it('should have OTP protocol defined', () => {
expect(Protocols.OTP).toBe('OTP');
});
});
describe('Issuers', () => {
it('should have MosipOtp issuer defined', () => {
expect(Issuers.MosipOtp).toBe('MosipOtp');
});
it('should have Mosip issuer defined', () => {
expect(Issuers.Mosip).toBe('Mosip');
});
});
describe('ACTIVATION_NEEDED', () => {
it('should contain Mosip', () => {
expect(ACTIVATION_NEEDED).toContain(Issuers.Mosip);
});
it('should contain MosipOtp', () => {
expect(ACTIVATION_NEEDED).toContain(Issuers.MosipOtp);
});
it('should have exactly 2 issuers', () => {
expect(ACTIVATION_NEEDED).toHaveLength(2);
});
});
describe('isActivationNeeded', () => {
it('should return true for Mosip issuer', () => {
expect(isActivationNeeded('Mosip')).toBe(true);
});
it('should return true for MosipOtp issuer', () => {
expect(isActivationNeeded('MosipOtp')).toBe(true);
});
it('should return false for other issuers', () => {
expect(isActivationNeeded('SomeOtherIssuer')).toBe(false);
});
it('should return false for empty string', () => {
expect(isActivationNeeded('')).toBe(false);
});
it('should return false for undefined', () => {
expect(isActivationNeeded(undefined as any)).toBe(false);
});
it('should be case sensitive', () => {
expect(isActivationNeeded('mosip')).toBe(false);
expect(isActivationNeeded('MOSIP')).toBe(false);
});
});
describe('Issuers_Key_Ref', () => {
it('should have correct key reference', () => {
expect(Issuers_Key_Ref).toBe('OpenId4VCI_KeyPair');
});
});
describe('getDisplayObjectForCurrentLanguage', () => {
it('should return display object for current language', () => {
const display = [
{language: 'en', name: 'English Name', logo: 'en-logo.png'},
{language: 'hi', name: 'Hindi Name', logo: 'hi-logo.png'},
] as any;
const result = getDisplayObjectForCurrentLanguage(display);
expect(result).toBeDefined();
expect(result.name).toBeDefined();
});
it('should return first display object when language not found', () => {
const display = [
{language: 'fr', name: 'French Name', logo: 'fr-logo.png'},
{language: 'de', name: 'German Name', logo: 'de-logo.png'},
] as any;
const result = getDisplayObjectForCurrentLanguage(display);
expect(result).toBeDefined();
expect(result.name).toBe('French Name');
});
it('should return empty object when display array is empty', () => {
const result = getDisplayObjectForCurrentLanguage([]);
expect(result).toEqual({});
});
it('should return empty object when display is null', () => {
const result = getDisplayObjectForCurrentLanguage(null as any);
expect(result).toEqual({});
});
it('should handle locale key instead of language key', () => {
const display = [
{locale: 'en-US', name: 'English Name', logo: 'en-logo.png'},
{locale: 'hi-IN', name: 'Hindi Name', logo: 'hi-logo.png'},
] as any;
const result = getDisplayObjectForCurrentLanguage(display);
expect(result).toBeDefined();
});
it('should fallback to en-US when current language not found', () => {
const display = [
{language: 'fr', name: 'French Name'},
{language: 'en-US', name: 'English US Name'},
] as any;
const result = getDisplayObjectForCurrentLanguage(display);
expect(result.name).toBe('English US Name');
});
});
describe('removeBottomSectionFields', () => {
it('should remove bottom section fields for SD-JWT format', () => {
const fields = ['name', 'age', 'photo', 'signature', 'address'];
const result = removeBottomSectionFields(fields, VCFormat.vc_sd_jwt);
expect(result).toBeDefined();
expect(Array.isArray(result)).toBe(true);
});
it('should remove bottom section fields for DC-SD-JWT format', () => {
const fields = ['name', 'age', 'photo', 'signature'];
const result = removeBottomSectionFields(fields, VCFormat.dc_sd_jwt);
expect(result).toBeDefined();
expect(Array.isArray(result)).toBe(true);
});
it('should remove address field for LDP format', () => {
const fields = ['name', 'age', 'address', 'photo'];
const result = removeBottomSectionFields(fields, VCFormat.ldp_vc);
expect(result).toBeDefined();
expect(result).not.toContain('address');
});
it('should handle empty fields array', () => {
const result = removeBottomSectionFields([], VCFormat.ldp_vc);
expect(result).toEqual([]);
});
});
describe('getMatchingCredentialIssuerMetadata', () => {
it('should return matching credential configuration', () => {
const wellknown = {
credential_configurations_supported: {
MOSIPVerifiableCredential: {
format: 'ldp_vc',
order: ['name', 'age'],
},
AnotherCredential: {
format: 'jwt_vc',
},
},
};
const result = getMatchingCredentialIssuerMetadata(
wellknown,
'MOSIPVerifiableCredential',
);
expect(result).toBeDefined();
expect(result.format).toBe('ldp_vc');
expect(result.order).toEqual(['name', 'age']);
});
it('should throw error when credential type not found', () => {
const wellknown = {
credential_configurations_supported: {
SomeCredential: {format: 'ldp_vc'},
},
};
expect(() => {
getMatchingCredentialIssuerMetadata(wellknown, 'NonExistentCredential');
}).toThrow();
});
it('should handle multiple credential configurations', () => {
const wellknown = {
credential_configurations_supported: {
Credential1: {format: 'ldp_vc'},
Credential2: {format: 'jwt_vc'},
Credential3: {format: 'mso_mdoc'},
},
};
const result = getMatchingCredentialIssuerMetadata(
wellknown,
'Credential2',
);
expect(result).toBeDefined();
expect(result.format).toBe('jwt_vc');
});
});
describe('selectCredentialRequestKey', () => {
it('should select first supported key type', () => {
const proofSigningAlgos = ['RS256', 'ES256'];
const keyOrder = {'0': 'RS256', '1': 'ES256', '2': 'Ed25519'};
const result = selectCredentialRequestKey(proofSigningAlgos, keyOrder);
expect(result).toBe('RS256');
});
it('should return first key when no match found', () => {
const proofSigningAlgos = ['UNKNOWN_ALGO'];
const keyOrder = {'0': 'RS256', '1': 'ES256'};
const result = selectCredentialRequestKey(proofSigningAlgos, keyOrder);
expect(result).toBe('RS256');
});
it('should handle empty proofSigningAlgos', () => {
const keyOrder = {'0': 'RS256', '1': 'ES256'};
const result = selectCredentialRequestKey([], keyOrder);
expect(result).toBe('RS256');
});
it('should select matching key from middle of order', () => {
const proofSigningAlgos = ['ES256'];
const keyOrder = {'0': 'RS256', '1': 'ES256', '2': 'Ed25519'};
const result = selectCredentialRequestKey(proofSigningAlgos, keyOrder);
expect(result).toBe('ES256');
});
});
describe('updateCredentialInformation', () => {
it('should update credential information for MSO_MDOC format', async () => {
const mockContext = {
selectedCredentialType: {
id: 'TestCredential',
format: VCFormat.mso_mdoc,
},
selectedIssuer: {
display: [{language: 'en', logo: 'test-logo.png'}],
},
vcMetadata: {
id: 'test-id',
},
};
const mockCredential = {
credential: 'test-credential-data',
} as any;
const result = await updateCredentialInformation(
mockContext,
mockCredential,
);
expect(result).toBeDefined();
expect(result.format).toBe(VCFormat.mso_mdoc);
expect(result.verifiableCredential).toBeDefined();
expect(result.verifiableCredential.credentialConfigurationId).toBe(
'TestCredential',
);
expect(result.generatedOn).toBeInstanceOf(Date);
});
it('should update credential information for SD-JWT format', async () => {
const mockContext = {
selectedCredentialType: {
id: 'SDJWTCredential',
format: VCFormat.vc_sd_jwt,
},
selectedIssuer: {
display: [{language: 'en', logo: 'sd-jwt-logo.png'}],
},
vcMetadata: {
id: 'sd-jwt-id',
},
};
const mockCredential = {
credential: 'sd-jwt-credential-data',
} as any;
const result = await updateCredentialInformation(
mockContext,
mockCredential,
);
expect(result).toBeDefined();
expect(result.format).toBe(VCFormat.vc_sd_jwt);
expect(result.vcMetadata.format).toBe(VCFormat.vc_sd_jwt);
});
it('should update credential information for DC-SD-JWT format', async () => {
const mockContext = {
selectedCredentialType: {
id: 'DCSDJWTCredential',
format: VCFormat.dc_sd_jwt,
},
selectedIssuer: {
display: [{language: 'en', logo: 'dc-logo.png'}],
},
vcMetadata: {
id: 'dc-jwt-id',
},
};
const mockCredential = {
credential: 'dc-sd-jwt-credential-data',
} as any;
const result = await updateCredentialInformation(
mockContext,
mockCredential,
);
expect(result).toBeDefined();
expect(result.format).toBe(VCFormat.dc_sd_jwt);
});
it('should handle credential without logo in display', async () => {
const mockContext = {
selectedCredentialType: {
id: 'NoLogoCredential',
format: VCFormat.ldp_vc,
},
selectedIssuer: {
display: [{language: 'en'}],
},
vcMetadata: {},
};
const mockCredential = {
credential: 'no-logo-credential',
} as any;
const result = await updateCredentialInformation(
mockContext,
mockCredential,
);
expect(result).toBeDefined();
expect(result.verifiableCredential.issuerLogo).toBe('');
});
it('should include vcMetadata with format', async () => {
const mockContext = {
selectedCredentialType: {
id: 'MetadataTest',
format: VCFormat.ldp_vc,
},
selectedIssuer: {
display: [],
},
vcMetadata: {
id: 'metadata-id',
},
};
const mockCredential = {
credential: 'metadata-test',
} as any;
const result = await updateCredentialInformation(
mockContext,
mockCredential,
);
expect(result.vcMetadata).toBeDefined();
expect(result.vcMetadata.format).toBe(VCFormat.ldp_vc);
expect(result.vcMetadata.id).toBe('metadata-id');
});
});
});

View File

@@ -0,0 +1,97 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import {shareImageToAllSupportedApps} from './imageUtils';
import RNShare from 'react-native-share';
jest.mock('react-native-share', () => ({
open: jest.fn(),
}));
describe('imageUtils', () => {
describe('shareImageToAllSupportedApps', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('should return true when sharing is successful', async () => {
const mockShareOptions = {
url: 'file://path/to/image.jpg',
type: 'image/jpeg',
};
(RNShare.open as jest.Mock).mockResolvedValue({success: true});
const result = await shareImageToAllSupportedApps(mockShareOptions);
expect(result).toBe(true);
expect(RNShare.open).toHaveBeenCalledWith(mockShareOptions);
});
it('should return false when sharing fails', async () => {
const mockShareOptions = {
url: 'file://path/to/image.jpg',
type: 'image/jpeg',
};
(RNShare.open as jest.Mock).mockResolvedValue({success: false});
const result = await shareImageToAllSupportedApps(mockShareOptions);
expect(result).toBe(false);
});
it('should return false when an exception occurs', async () => {
const mockShareOptions = {
url: 'file://path/to/image.jpg',
type: 'image/jpeg',
};
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
(RNShare.open as jest.Mock).mockRejectedValue(new Error('Share failed'));
const result = await shareImageToAllSupportedApps(mockShareOptions);
expect(result).toBe(false);
expect(consoleErrorSpy).toHaveBeenCalledWith(
'Exception while sharing image::',
expect.any(Error),
);
consoleErrorSpy.mockRestore();
});
it('should handle different share options', async () => {
const mockShareOptions = {
url: 'file://path/to/qr-code.png',
type: 'image/png',
title: 'Share QR Code',
};
(RNShare.open as jest.Mock).mockResolvedValue({success: true});
const result = await shareImageToAllSupportedApps(mockShareOptions);
expect(result).toBe(true);
expect(RNShare.open).toHaveBeenCalledWith(mockShareOptions);
});
it('should handle user dismissing share dialog', async () => {
const mockShareOptions = {
url: 'file://path/to/image.jpg',
};
(RNShare.open as jest.Mock).mockRejectedValue({
message: 'User did not share',
});
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
const result = await shareImageToAllSupportedApps(mockShareOptions);
expect(result).toBe(false);
expect(consoleErrorSpy).toHaveBeenCalledWith(
'Exception while sharing image::',
expect.objectContaining({message: 'User did not share'}),
);
consoleErrorSpy.mockRestore();
});
});
});