mirror of
https://github.com/selfxyz/self.git
synced 2026-02-19 02:24:25 -05:00
SELF-1754: Implement selective disclosure on Proving Screen (#1549)
* add document selector test screen * clean up mock docs * update selection options * Add DocumentSelectorForProving screen and route proof flows through it (#1555) * Add document selector to proving flow * fix formatting * improvements * redirect user to document not found screen when no documents * option flow tweaks and tests * wip tweaks * fix scrollview bottom padding (#1556) * tighten up selection text * create inerstitial * save wip * remove not accepted state * save wip design * formatting * update design * update layout * Update proving flow tests (#1559) * Refactor ProveScreen to ProofRequestCard layout and preserve scroll position (#1560) * Refactor prove screen layout * fix: amount of hooks rendered needs to be the same for all variants * long URL ellipsis * keep titles consistent * lint --------- Co-authored-by: Leszek Stachowski <leszek.stachowski@self.xyz> * wip fix tests * fix tests * formatting * agent feedback * fix tests * save wip * remove text * fix types * save working header update * no transition * cache document load for proving flow * save fixes * small fixes * match disclosure text * design updates * fix approve flow * fix document type flash * add min height so text doesn't jump * update lock * formatting * save refactor wip * don't enable euclid yet * fix tests * fix staleness check * fix select box description * remove id selector screen * vertically center * button updates * Remove proving document cache (#1567) * formatting --------- Co-authored-by: Leszek Stachowski <leszek.stachowski@self.xyz>
This commit is contained in:
@@ -254,7 +254,7 @@ export const HeldPrimaryButtonProveScreen: React.FC<HeldPrimaryButtonProveScreen
|
||||
);
|
||||
}
|
||||
if (state.matches('ready')) {
|
||||
return 'Hold to verify';
|
||||
return 'Press and hold to verify';
|
||||
}
|
||||
if (state.matches('verifying')) {
|
||||
return (
|
||||
|
||||
@@ -19,6 +19,9 @@ export const cyan300 = '#67E8F9';
|
||||
export const emerald500 = '#10B981';
|
||||
|
||||
export const green500 = '#22C55E';
|
||||
export const green600 = '#16A34A';
|
||||
|
||||
export const iosSeparator = 'rgba(60,60,67,0.36)';
|
||||
|
||||
export const neutral400 = '#A3A3A3';
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@ export {
|
||||
cyan300,
|
||||
emerald500,
|
||||
green500,
|
||||
green600,
|
||||
iosSeparator,
|
||||
neutral400,
|
||||
neutral700,
|
||||
red500,
|
||||
|
||||
190
packages/mobile-sdk-alpha/src/documents/validation.ts
Normal file
190
packages/mobile-sdk-alpha/src/documents/validation.ts
Normal file
@@ -0,0 +1,190 @@
|
||||
// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import type { AadhaarData, DocumentMetadata, IDDocument } from '@selfxyz/common';
|
||||
import { attributeToPosition, attributeToPosition_ID } from '@selfxyz/common/constants';
|
||||
import type { PassportData } from '@selfxyz/common/types/passport';
|
||||
import type { DocumentCatalog } from '@selfxyz/common/utils/types';
|
||||
import { isAadhaarDocument, isMRZDocument } from '@selfxyz/common/utils/types';
|
||||
|
||||
export interface DocumentAttributes {
|
||||
nameSlice: string;
|
||||
dobSlice: string;
|
||||
yobSlice: string;
|
||||
issuingStateSlice: string;
|
||||
nationalitySlice: string;
|
||||
passNoSlice: string;
|
||||
sexSlice: string;
|
||||
expiryDateSlice: string;
|
||||
isPassportType: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a document expiration date (in YYMMDD format) has passed.
|
||||
* We assume dateOfExpiry is this century because ICAO standard for biometric passport
|
||||
* became standard around 2002.
|
||||
*
|
||||
* @param dateOfExpiry - Expiration date in YYMMDD format from MRZ
|
||||
* @returns true if the document is expired, false otherwise
|
||||
*/
|
||||
export function checkDocumentExpiration(dateOfExpiry: string): boolean {
|
||||
if (!dateOfExpiry || dateOfExpiry.length !== 6) {
|
||||
return false; // Invalid format, don't treat as expired
|
||||
}
|
||||
|
||||
const year = parseInt(dateOfExpiry.slice(0, 2), 10);
|
||||
const fullyear = 2000 + year;
|
||||
const month = parseInt(dateOfExpiry.slice(2, 4), 10) - 1; // JS months are 0-indexed
|
||||
const day = parseInt(dateOfExpiry.slice(4, 6), 10);
|
||||
|
||||
const expiryDateUTC = new Date(Date.UTC(fullyear, month, day, 0, 0, 0, 0));
|
||||
const nowUTC = new Date();
|
||||
const todayUTC = new Date(Date.UTC(nowUTC.getFullYear(), nowUTC.getMonth(), nowUTC.getDate(), 0, 0, 0, 0));
|
||||
|
||||
return todayUTC >= expiryDateUTC;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts attributes from Aadhaar document data
|
||||
*/
|
||||
function getAadhaarAttributes(document: AadhaarData): DocumentAttributes {
|
||||
const extractedFields = document.extractedFields;
|
||||
// For Aadhaar, we format the name to work with the existing getNameAndSurname function
|
||||
// We'll put the full name in the "surname" position and leave names empty
|
||||
const fullName = extractedFields?.name || '';
|
||||
const nameSliceFormatted = fullName ? `${fullName}<<` : ''; // Format like MRZ
|
||||
|
||||
// Format DOB to YYMMDD for consistency with passport format
|
||||
let dobFormatted = '';
|
||||
if (extractedFields?.dob && extractedFields?.mob && extractedFields?.yob) {
|
||||
const year = extractedFields.yob.length === 4 ? extractedFields.yob.slice(-2) : extractedFields.yob;
|
||||
const month = extractedFields.mob.padStart(2, '0');
|
||||
const day = extractedFields.dob.padStart(2, '0');
|
||||
dobFormatted = `${year}${month}${day}`;
|
||||
}
|
||||
|
||||
return {
|
||||
nameSlice: nameSliceFormatted,
|
||||
dobSlice: dobFormatted,
|
||||
yobSlice: extractedFields?.yob || '',
|
||||
issuingStateSlice: extractedFields?.state || '',
|
||||
nationalitySlice: 'IND', // Aadhaar is always Indian
|
||||
passNoSlice: extractedFields?.aadhaarLast4Digits || '',
|
||||
sexSlice:
|
||||
extractedFields?.gender === 'M' ? 'M' : extractedFields?.gender === 'F' ? 'F' : extractedFields?.gender || '',
|
||||
expiryDateSlice: '', // Aadhaar doesn't expire
|
||||
isPassportType: false,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts attributes from MRZ string (passport or ID card)
|
||||
*/
|
||||
function getPassportAttributes(mrz: string, documentCategory: string): DocumentAttributes {
|
||||
const isPassportType = documentCategory === 'passport';
|
||||
const attributePositions = isPassportType ? attributeToPosition : attributeToPosition_ID;
|
||||
|
||||
const nameSlice = mrz.slice(attributePositions.name[0], attributePositions.name[1]);
|
||||
const dobSlice = mrz.slice(attributePositions.date_of_birth[0], attributePositions.date_of_birth[1] + 1);
|
||||
const yobSlice = mrz.slice(attributePositions.date_of_birth[0], attributePositions.date_of_birth[0] + 2);
|
||||
const issuingStateSlice = mrz.slice(attributePositions.issuing_state[0], attributePositions.issuing_state[1] + 1);
|
||||
const nationalitySlice = mrz.slice(attributePositions.nationality[0], attributePositions.nationality[1] + 1);
|
||||
const passNoSlice = mrz.slice(attributePositions.passport_number[0], attributePositions.passport_number[1] + 1);
|
||||
const sexSlice = mrz.slice(attributePositions.gender[0], attributePositions.gender[1] + 1);
|
||||
const expiryDateSlice = mrz.slice(attributePositions.expiry_date[0], attributePositions.expiry_date[1] + 1);
|
||||
return {
|
||||
nameSlice,
|
||||
dobSlice,
|
||||
yobSlice,
|
||||
issuingStateSlice,
|
||||
nationalitySlice,
|
||||
passNoSlice,
|
||||
sexSlice,
|
||||
expiryDateSlice,
|
||||
isPassportType,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts document attributes from passport, ID card, or Aadhaar data.
|
||||
*
|
||||
* @param document - Document data (PassportData, AadhaarData, or IDDocument)
|
||||
* @returns Document attributes including name, DOB, expiry date, etc.
|
||||
*/
|
||||
export function getDocumentAttributes(document: PassportData | AadhaarData): DocumentAttributes {
|
||||
if (isAadhaarDocument(document)) {
|
||||
return getAadhaarAttributes(document);
|
||||
} else if (isMRZDocument(document)) {
|
||||
return getPassportAttributes(document.mrz, document.documentCategory);
|
||||
} else {
|
||||
// Fallback for unknown document types
|
||||
return {
|
||||
nameSlice: '',
|
||||
dobSlice: '',
|
||||
yobSlice: '',
|
||||
issuingStateSlice: '',
|
||||
nationalitySlice: '',
|
||||
passNoSlice: '',
|
||||
sexSlice: '',
|
||||
expiryDateSlice: '',
|
||||
isPassportType: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a document is valid for use in proving flows.
|
||||
* A document is valid if it is not expired.
|
||||
* Mock documents are considered valid for testing with staging environments.
|
||||
*
|
||||
* @param metadata - Document metadata from catalog
|
||||
* @param documentData - Full document data (optional, used for expiry check)
|
||||
* @returns true if document can be used for proving
|
||||
*/
|
||||
export function isDocumentValidForProving(metadata: DocumentMetadata, documentData?: IDDocument): boolean {
|
||||
// Check if expired
|
||||
if (documentData) {
|
||||
try {
|
||||
const attributes = getDocumentAttributes(documentData);
|
||||
if (attributes.expiryDateSlice && checkDocumentExpiration(attributes.expiryDateSlice)) {
|
||||
return false;
|
||||
}
|
||||
} catch {
|
||||
// If we can't check expiry, assume valid
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Picks the best document to auto-select from a catalog.
|
||||
* Prefers the currently selected document if valid, otherwise picks the first valid one.
|
||||
*
|
||||
* @param catalog - Document catalog
|
||||
* @param documents - Map of document ID to document data
|
||||
* @returns Document ID to select, or undefined if no valid documents
|
||||
*/
|
||||
export function pickBestDocumentToSelect(
|
||||
catalog: DocumentCatalog,
|
||||
documents: Record<string, { data: IDDocument; metadata: DocumentMetadata }>,
|
||||
): string | undefined {
|
||||
// Check if currently selected document is valid
|
||||
if (catalog.selectedDocumentId) {
|
||||
const selectedMeta = catalog.documents.find(doc => doc.id === catalog.selectedDocumentId);
|
||||
const selectedData = selectedMeta ? documents[catalog.selectedDocumentId] : undefined;
|
||||
|
||||
if (selectedMeta && isDocumentValidForProving(selectedMeta, selectedData?.data)) {
|
||||
return catalog.selectedDocumentId;
|
||||
}
|
||||
}
|
||||
|
||||
// Find first valid document
|
||||
const firstValid = catalog.documents.find(doc => {
|
||||
const docData = documents[doc.id];
|
||||
return isDocumentValidForProving(doc, docData?.data);
|
||||
});
|
||||
|
||||
return firstValid?.id;
|
||||
}
|
||||
@@ -33,6 +33,8 @@ export type { BaseContext, NFCScanContext, ProofContext } from './proving/intern
|
||||
|
||||
export type { DG1, DG2, ParsedNFCResponse } from './nfc';
|
||||
|
||||
export type { DocumentAttributes } from './documents/validation';
|
||||
|
||||
export type { DocumentData, DocumentMetadata, PassportCameraProps, ScreenProps } from './types/ui';
|
||||
|
||||
export type { HapticOptions, HapticType } from './haptic/shared';
|
||||
@@ -97,7 +99,13 @@ export {
|
||||
triggerFeedback,
|
||||
} from './haptic';
|
||||
|
||||
/** @deprecated Use createSelfClient().extractMRZInfo or import from './mrz' */
|
||||
export {
|
||||
checkDocumentExpiration,
|
||||
getDocumentAttributes,
|
||||
isDocumentValidForProving,
|
||||
pickBestDocumentToSelect,
|
||||
} from './documents/validation';
|
||||
|
||||
export {
|
||||
clearPassportData,
|
||||
getAllDocuments,
|
||||
@@ -114,9 +122,10 @@ export { defaultConfig } from './config/defaults';
|
||||
|
||||
export { defaultOptions } from './haptic/shared';
|
||||
|
||||
export { extractMRZInfo, extractNameFromMRZ, formatDateToYYMMDD } from './mrz';
|
||||
|
||||
/** @deprecated Use createSelfClient().extractMRZInfo or import from './mrz' */
|
||||
export { extractMRZInfo } from './mrz';
|
||||
export { extractNameFromDocument } from './documents/utils';
|
||||
export { extractNameFromMRZ, formatDateToYYMMDD } from './mrz';
|
||||
|
||||
export { generateMockDocument, signatureAlgorithmToStrictSignatureAlgorithm } from './mock/generator';
|
||||
|
||||
|
||||
314
packages/mobile-sdk-alpha/tests/documents/validation.test.ts
Normal file
314
packages/mobile-sdk-alpha/tests/documents/validation.test.ts
Normal file
@@ -0,0 +1,314 @@
|
||||
// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import type { DocumentCatalog, DocumentMetadata } from '@selfxyz/common/types';
|
||||
import type { PassportData } from '@selfxyz/common/types/passport';
|
||||
|
||||
import {
|
||||
checkDocumentExpiration,
|
||||
isDocumentValidForProving,
|
||||
pickBestDocumentToSelect,
|
||||
} from '../../src/documents/validation';
|
||||
|
||||
describe('checkDocumentExpiration', () => {
|
||||
it('returns false for invalid format (too short)', () => {
|
||||
expect(checkDocumentExpiration('1234')).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false for invalid format (too long)', () => {
|
||||
expect(checkDocumentExpiration('1234567')).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false for empty string', () => {
|
||||
expect(checkDocumentExpiration('')).toBe(false);
|
||||
});
|
||||
|
||||
it('returns true for expired date (past date)', () => {
|
||||
// Date in 2020
|
||||
expect(checkDocumentExpiration('200101')).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false for future date', () => {
|
||||
// Date in 2050
|
||||
expect(checkDocumentExpiration('500101')).toBe(false);
|
||||
});
|
||||
|
||||
it('returns true for today (expired as of today)', () => {
|
||||
const now = new Date();
|
||||
const year = now.getFullYear().toString().slice(-2);
|
||||
const month = (now.getMonth() + 1).toString().padStart(2, '0');
|
||||
const day = now.getDate().toString().padStart(2, '0');
|
||||
const today = `${year}${month}${day}`;
|
||||
// Document that expires today is considered expired
|
||||
expect(checkDocumentExpiration(today)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true for yesterday (expired)', () => {
|
||||
const yesterday = new Date();
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
const year = yesterday.getFullYear().toString().slice(-2);
|
||||
const month = (yesterday.getMonth() + 1).toString().padStart(2, '0');
|
||||
const day = yesterday.getDate().toString().padStart(2, '0');
|
||||
const yesterdayStr = `${year}${month}${day}`;
|
||||
expect(checkDocumentExpiration(yesterdayStr)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isDocumentValidForProving', () => {
|
||||
const mockMetadata: DocumentMetadata = {
|
||||
id: 'test-id',
|
||||
documentType: 'passport',
|
||||
documentCategory: 'passport',
|
||||
data: 'mock-data',
|
||||
mock: false,
|
||||
};
|
||||
|
||||
it('returns true for document without data (cannot check expiry)', () => {
|
||||
expect(isDocumentValidForProving(mockMetadata)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true for mock document', () => {
|
||||
const mockDoc: DocumentMetadata = {
|
||||
...mockMetadata,
|
||||
mock: true,
|
||||
};
|
||||
expect(isDocumentValidForProving(mockDoc)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true for valid passport with future expiry', () => {
|
||||
// MRZ with expiry date 501231 (December 31, 2050)
|
||||
const validPassport: PassportData = {
|
||||
mrz: 'P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L898902C36UTO7408122F5012319ZE184226B<<<<<10',
|
||||
dsc: 'mock-dsc',
|
||||
eContent: [1, 2, 3],
|
||||
signedAttr: [1, 2, 3],
|
||||
encryptedDigest: [1, 2, 3],
|
||||
documentType: 'passport',
|
||||
documentCategory: 'passport',
|
||||
mock: false,
|
||||
};
|
||||
|
||||
expect(isDocumentValidForProving(mockMetadata, validPassport)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false for expired passport', () => {
|
||||
// Passport expired in 2012
|
||||
const expiredPassport: PassportData = {
|
||||
mrz: 'P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L898902C36UTO7408122F1204159ZE184226B<<<<<10',
|
||||
dsc: 'mock-dsc',
|
||||
eContent: [1, 2, 3],
|
||||
signedAttr: [1, 2, 3],
|
||||
encryptedDigest: [1, 2, 3],
|
||||
documentType: 'passport',
|
||||
documentCategory: 'passport',
|
||||
mock: false,
|
||||
};
|
||||
|
||||
// Modify MRZ to have expired date (120415 = April 15, 2012)
|
||||
const mrzWithExpiredDate =
|
||||
'P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L898902C36UTO7408122F1204159ZE184226B<<<<<10';
|
||||
expiredPassport.mrz = mrzWithExpiredDate.slice(0, 57) + '120415' + mrzWithExpiredDate.slice(63);
|
||||
|
||||
expect(isDocumentValidForProving(mockMetadata, expiredPassport)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns true if getDocumentAttributes throws error', () => {
|
||||
const invalidDocument = {
|
||||
documentType: 'passport',
|
||||
documentCategory: 'passport',
|
||||
mock: false,
|
||||
} as any;
|
||||
|
||||
expect(isDocumentValidForProving(mockMetadata, invalidDocument)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('pickBestDocumentToSelect', () => {
|
||||
// MRZ with expiry date 501231 (December 31, 2050)
|
||||
const validPassport: PassportData = {
|
||||
mrz: 'P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L898902C36UTO7408122F5012319ZE184226B<<<<<10',
|
||||
dsc: 'mock-dsc',
|
||||
eContent: [1, 2, 3],
|
||||
signedAttr: [1, 2, 3],
|
||||
encryptedDigest: [1, 2, 3],
|
||||
documentType: 'passport',
|
||||
documentCategory: 'passport',
|
||||
mock: false,
|
||||
};
|
||||
|
||||
// MRZ with expiry date 120415 (April 15, 2012 - expired)
|
||||
const expiredPassport: PassportData = {
|
||||
...validPassport,
|
||||
mrz: 'P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L898902C36UTO7408122F1204159ZE184226B<<<<<10',
|
||||
};
|
||||
|
||||
it('returns undefined for empty catalog', () => {
|
||||
const catalog: DocumentCatalog = {
|
||||
documents: [],
|
||||
};
|
||||
expect(pickBestDocumentToSelect(catalog, {})).toBeUndefined();
|
||||
});
|
||||
|
||||
it('returns currently selected document if valid', () => {
|
||||
const metadata: DocumentMetadata = {
|
||||
id: 'doc1',
|
||||
documentType: 'passport',
|
||||
documentCategory: 'passport',
|
||||
data: 'data1',
|
||||
mock: false,
|
||||
};
|
||||
|
||||
const catalog: DocumentCatalog = {
|
||||
documents: [metadata],
|
||||
selectedDocumentId: 'doc1',
|
||||
};
|
||||
|
||||
const documents = {
|
||||
doc1: { data: validPassport, metadata },
|
||||
};
|
||||
|
||||
expect(pickBestDocumentToSelect(catalog, documents)).toBe('doc1');
|
||||
});
|
||||
|
||||
it('returns first valid document if currently selected is expired', () => {
|
||||
const expiredMetadata: DocumentMetadata = {
|
||||
id: 'doc1',
|
||||
documentType: 'passport',
|
||||
documentCategory: 'passport',
|
||||
data: 'data1',
|
||||
mock: false,
|
||||
};
|
||||
|
||||
const validMetadata: DocumentMetadata = {
|
||||
id: 'doc2',
|
||||
documentType: 'passport',
|
||||
documentCategory: 'passport',
|
||||
data: 'data2',
|
||||
mock: false,
|
||||
};
|
||||
|
||||
const catalog: DocumentCatalog = {
|
||||
documents: [expiredMetadata, validMetadata],
|
||||
selectedDocumentId: 'doc1',
|
||||
};
|
||||
|
||||
const documents = {
|
||||
doc1: { data: expiredPassport, metadata: expiredMetadata },
|
||||
doc2: { data: validPassport, metadata: validMetadata },
|
||||
};
|
||||
|
||||
expect(pickBestDocumentToSelect(catalog, documents)).toBe('doc2');
|
||||
});
|
||||
|
||||
it('returns first valid document if no document is selected', () => {
|
||||
const metadata1: DocumentMetadata = {
|
||||
id: 'doc1',
|
||||
documentType: 'passport',
|
||||
documentCategory: 'passport',
|
||||
data: 'data1',
|
||||
mock: false,
|
||||
};
|
||||
|
||||
const metadata2: DocumentMetadata = {
|
||||
id: 'doc2',
|
||||
documentType: 'passport',
|
||||
documentCategory: 'passport',
|
||||
data: 'data2',
|
||||
mock: false,
|
||||
};
|
||||
|
||||
const catalog: DocumentCatalog = {
|
||||
documents: [metadata1, metadata2],
|
||||
};
|
||||
|
||||
const documents = {
|
||||
doc1: { data: validPassport, metadata: metadata1 },
|
||||
doc2: { data: validPassport, metadata: metadata2 },
|
||||
};
|
||||
|
||||
expect(pickBestDocumentToSelect(catalog, documents)).toBe('doc1');
|
||||
});
|
||||
|
||||
it('returns undefined if all documents are expired', () => {
|
||||
const metadata: DocumentMetadata = {
|
||||
id: 'doc1',
|
||||
documentType: 'passport',
|
||||
documentCategory: 'passport',
|
||||
data: 'data1',
|
||||
mock: false,
|
||||
};
|
||||
|
||||
const catalog: DocumentCatalog = {
|
||||
documents: [metadata],
|
||||
};
|
||||
|
||||
const documents = {
|
||||
doc1: { data: expiredPassport, metadata },
|
||||
};
|
||||
|
||||
expect(pickBestDocumentToSelect(catalog, documents)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('selects mock document if it is the only option', () => {
|
||||
const mockMetadata: DocumentMetadata = {
|
||||
id: 'doc1',
|
||||
documentType: 'passport',
|
||||
documentCategory: 'passport',
|
||||
data: 'mock-data',
|
||||
mock: true,
|
||||
};
|
||||
|
||||
const catalog: DocumentCatalog = {
|
||||
documents: [mockMetadata],
|
||||
};
|
||||
|
||||
const mockPassport: PassportData = {
|
||||
...validPassport,
|
||||
mock: true,
|
||||
};
|
||||
|
||||
const documents = {
|
||||
doc1: { data: mockPassport, metadata: mockMetadata },
|
||||
};
|
||||
|
||||
expect(pickBestDocumentToSelect(catalog, documents)).toBe('doc1');
|
||||
});
|
||||
|
||||
it('prefers selected document even if it is mock', () => {
|
||||
const mockMetadata: DocumentMetadata = {
|
||||
id: 'mock1',
|
||||
documentType: 'passport',
|
||||
documentCategory: 'passport',
|
||||
data: 'mock-data',
|
||||
mock: true,
|
||||
};
|
||||
|
||||
const realMetadata: DocumentMetadata = {
|
||||
id: 'real1',
|
||||
documentType: 'passport',
|
||||
documentCategory: 'passport',
|
||||
data: 'real-data',
|
||||
mock: false,
|
||||
};
|
||||
|
||||
const catalog: DocumentCatalog = {
|
||||
documents: [mockMetadata, realMetadata],
|
||||
selectedDocumentId: 'mock1',
|
||||
};
|
||||
|
||||
const mockPassport: PassportData = {
|
||||
...validPassport,
|
||||
mock: true,
|
||||
};
|
||||
|
||||
const documents = {
|
||||
mock1: { data: mockPassport, metadata: mockMetadata },
|
||||
real1: { data: validPassport, metadata: realMetadata },
|
||||
};
|
||||
|
||||
expect(pickBestDocumentToSelect(catalog, documents)).toBe('mock1');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user