Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@
"import": "./dist/esm/src/utils/passports/passport_parsing/parseDscCertificateData.js",
"require": "./dist/cjs/src/utils/passports/passport_parsing/parseDscCertificateData.cjs"
},
"./utils/proving": {
"./utils/proving": {
"types": "./dist/esm/src/utils/proving.d.ts",
"import": "./dist/esm/src/utils/proving.js",
"require": "./dist/cjs/src/utils/proving.cjs"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@ export const PassportCameraScreen = ({ onMRZDetected }: PassportCameraProps) =>
passportNumber: 'L898902C3',
dateOfBirth: '740812',
dateOfExpiry: '120415',
surname: 'ERIKSSON',
givenNames: 'ANNA MARIA',
sex: 'F',
nationality: 'UTO',
issuingCountry: 'UTO',
documentType: 'P',
validation: {
Expand Down
98 changes: 75 additions & 23 deletions packages/mobile-sdk-alpha/src/processing/mrz.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
* Handles complex cases like: VAN<<DER<<BERG<<MARIA<ELENA
* Expected: surname="VAN DER BERG", givenNames="MARIA ELENA"
*/
function parseNames(nameField: string): { surname: string; givenNames: string } {

Check failure on line 53 in packages/mobile-sdk-alpha/src/processing/mrz.ts

View workflow job for this annotation

GitHub Actions / lint

'parseNames' is defined but never used. Allowed unused vars must match /^_/u
const parts = nameField.split('<<');

if (parts.length === 1) {
Expand Down Expand Up @@ -111,9 +111,19 @@
if (lines.length !== 2) {
return false;
}
const TD3_line_2_REGEX = /^([A-Z0-9<]{9})([0-9ILDSOG])([A-Z<]{3})/;
const isTD3 = TD3_line_2_REGEX.test(lines[1]);
return isTD3;
}

function validateTD1Format(lines: string[]): boolean {
console.log('validateTD1Format', lines);

// TD3 format: 2 lines, 44 characters each
return lines[0].length === 44 && lines[1].length === 44;
const concatenatedLines = lines[0] + lines[1];
const TD1_REGEX =
/^(?<documentType>[A-Z0-9<]{2})(?<issuingCountry>[A-Z<]{3})(?<documentNumber>[A-Z0-9<]{9})(?<checkDigitDocumentNumber>[0-9]{1})(?<optionalData1>[A-Z0-9<]{15})(?<dateOfBirth>[0-9]{6})(?<checkDigitDateOfBirth>[0-9]{1})(?<sex>[MF<]{1})(?<dateOfExpiry>[0-9]{6})(?<checkDigitDateOfExpiry>[0-9]{1})(?<nationality>[A-Z<]{3})(?<optionalData2>[A-Z0-9<]{7})/;
const isTD1 = TD1_REGEX.test(concatenatedLines) || lines[0].startsWith('I');
return isTD1;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove console logging and handle variable line counts (1/2/3) safely in TD1 format detection.

  • console.log leaks PII (document numbers, DOB, expiry) from user scans; disable in library code.
  • lines[0] + lines[1] will concatenate "undefined" if only one line is provided, which is brittle and can yield false positives/negatives.

Apply this diff to fix both:

-function validateTD1Format(lines: string[]): boolean {
-  console.log('validateTD1Format', lines);
-
-  const concatenatedLines = lines[0] + lines[1];
-  const TD1_REGEX =
-    /^(?<documentType>[A-Z0-9<]{2})(?<issuingCountry>[A-Z<]{3})(?<documentNumber>[A-Z0-9<]{9})(?<checkDigitDocumentNumber>[0-9]{1})(?<optionalData1>[A-Z0-9<]{15})(?<dateOfBirth>[0-9]{6})(?<checkDigitDateOfBirth>[0-9]{1})(?<sex>[MF<]{1})(?<dateOfExpiry>[0-9]{6})(?<checkDigitDateOfExpiry>[0-9]{1})(?<nationality>[A-Z<]{3})(?<optionalData2>[A-Z0-9<]{7})/;
-  const isTD1 = TD1_REGEX.test(concatenatedLines) || lines[0].startsWith('I');
-  return isTD1;
-}
+function validateTD1Format(lines: string[]): boolean {
+  // Join up to three TD1 lines defensively; tolerate single-line providers.
+  const concatenated = lines.slice(0, 3).join('').trim();
+  if (!concatenated) return false;
+  const TD1_REGEX =
+    /^(?<documentType>[A-Z0-9<]{2})(?<issuingCountry>[A-Z<]{3})(?<documentNumber>[A-Z0-9<]{9})(?<checkDigitDocumentNumber>\d)(?<optionalData1>[A-Z0-9<]{15})(?<dateOfBirth>\d{6})(?<checkDigitDateOfBirth>\d)(?<sex>[MF<])(?<dateOfExpiry>\d{6})(?<checkDigitDateOfExpiry>\d)(?<nationality>[A-Z<]{3})(?<optionalData2>[A-Z0-9<]{7})/;
+  // Allow a soft fallback for providers that only give the leading "ID" without full content.
+  return TD1_REGEX.test(concatenated) || concatenated.startsWith('ID');
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function validateTD1Format(lines: string[]): boolean {
console.log('validateTD1Format', lines);
// TD3 format: 2 lines, 44 characters each
return lines[0].length === 44 && lines[1].length === 44;
const concatenatedLines = lines[0] + lines[1];
const TD1_REGEX =
/^(?<documentType>[A-Z0-9<]{2})(?<issuingCountry>[A-Z<]{3})(?<documentNumber>[A-Z0-9<]{9})(?<checkDigitDocumentNumber>[0-9]{1})(?<optionalData1>[A-Z0-9<]{15})(?<dateOfBirth>[0-9]{6})(?<checkDigitDateOfBirth>[0-9]{1})(?<sex>[MF<]{1})(?<dateOfExpiry>[0-9]{6})(?<checkDigitDateOfExpiry>[0-9]{1})(?<nationality>[A-Z<]{3})(?<optionalData2>[A-Z0-9<]{7})/;
const isTD1 = TD1_REGEX.test(concatenatedLines) || lines[0].startsWith('I');
return isTD1;
}
function validateTD1Format(lines: string[]): boolean {
// Join up to three TD1 lines defensively; tolerate single-line providers.
const concatenated = lines.slice(0, 3).join('').trim();
if (!concatenated) return false;
const TD1_REGEX =
/^(?<documentType>[A-Z0-9<]{2})(?<issuingCountry>[A-Z<]{3})(?<documentNumber>[A-Z0-9<]{9})(?<checkDigitDocumentNumber>\d)(?<optionalData1>[A-Z0-9<]{15})(?<dateOfBirth>\d{6})(?<checkDigitDateOfBirth>\d)(?<sex>[MF<])(?<dateOfExpiry>\d{6})(?<checkDigitDateOfExpiry>\d)(?<nationality>[A-Z<]{3})(?<optionalData2>[A-Z0-9<]{7})/;
// Allow a soft fallback for providers that only give the leading "ID" without full content.
return TD1_REGEX.test(concatenated) || concatenated.startsWith('ID');
}
🤖 Prompt for AI Agents
In packages/mobile-sdk-alpha/src/processing/mrz.ts around lines 119 to 127,
remove the console.log call and guard against missing lines when detecting TD1:
stop logging PII, and build the input string safely (use lines[0] || '' and
lines[1] || '' or join only available lines) so you never concatenate
"undefined"; keep the existing regex and the fallback check
lines[0].startsWith('I') but ensure lines[0] exists before calling startsWith
(e.g. check lines.length > 0), and return the boolean result without any console
output.


/**
Expand All @@ -131,8 +141,6 @@
.slice(2, 5)
.replace(/</g, '')
.replace(/[^A-Z]/g, '');
const nameField = line1.slice(5, 44);
const { surname, givenNames } = parseNames(nameField);

// Line 2: PASSPORT(9)CHECK(1)NATIONALITY(3)DOB(6)DOBCHECK(1)SEX(1)EXPIRY(6)EXPIRYCHECK(1)OPTIONAL(7)FINALCHECK(1)
const passportNumber = line2.slice(0, 9).replace(/</g, '');
Expand All @@ -155,22 +163,55 @@
nationality = rawNat.slice(0, 3).replace(/[^A-Z]/g, '');
}
const dateOfBirth = line2.slice(13, 19);
const sex = line2.slice(20, 21).replace(/</g, '');
const dateOfExpiry = line2.slice(21, 27);

return {
documentType,
issuingCountry,
surname,
givenNames,
passportNumber,
nationality,
dateOfBirth,
sex,
dateOfExpiry,
};
}

function extractTD1Info(lines: string[]): Omit<MRZInfo, 'validation'> {
const line1 = lines[0];
const line2 = lines[1];

const concatenatedLines = line1 + line2;

return {
documentType: concatenatedLines.slice(0, 2),
issuingCountry: concatenatedLines.slice(2, 5),
passportNumber: concatenatedLines.slice(5, 14).replace(/</g, '').trim(),
dateOfBirth: concatenatedLines.slice(30, 36),
dateOfExpiry: concatenatedLines.slice(38, 44),
};
}
Comment on lines +116 to +129
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

TD1 extraction must not concatenate with possibly undefined lines; normalize input first.

Concatenating line1 + line2 will append "undefined" in single-line scenarios, skewing slices and risking incorrect field extraction.

Apply this diff:

-function extractTD1Info(lines: string[]): Omit<MRZInfo, 'validation'> {
-  const line1 = lines[0];
-  const line2 = lines[1];
-
-  const concatenatedLines = line1 + line2;
-
-  return {
-    documentType: concatenatedLines.slice(0, 2),
-    issuingCountry: concatenatedLines.slice(2, 5),
-    passportNumber: concatenatedLines.slice(5, 14).replace(/</g, '').trim(),
-    dateOfBirth: concatenatedLines.slice(30, 36),
-    dateOfExpiry: concatenatedLines.slice(38, 44),
-  };
-}
+function extractTD1Info(lines: string[]): Omit<MRZInfo, 'validation'> {
+  const td1 = lines.slice(0, 3).join(''); // handle 1/2/3-line input
+  return {
+    documentType: td1.slice(0, 2),
+    issuingCountry: td1.slice(2, 5).replace(/</g, ''),
+    passportNumber: td1.slice(5, 14).replace(/</g, ''),
+    dateOfBirth: td1.slice(30, 36),
+    dateOfExpiry: td1.slice(38, 44),
+  };
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function extractTD1Info(lines: string[]): Omit<MRZInfo, 'validation'> {
const line1 = lines[0];
const line2 = lines[1];
const concatenatedLines = line1 + line2;
return {
documentType: concatenatedLines.slice(0, 2),
issuingCountry: concatenatedLines.slice(2, 5),
passportNumber: concatenatedLines.slice(5, 14).replace(/</g, '').trim(),
dateOfBirth: concatenatedLines.slice(30, 36),
dateOfExpiry: concatenatedLines.slice(38, 44),
};
}
function extractTD1Info(lines: string[]): Omit<MRZInfo, 'validation'> {
const td1 = lines.slice(0, 3).join(''); // handle 1/2/3-line input
return {
documentType: td1.slice(0, 2),
issuingCountry: td1.slice(2, 5).replace(/</g, ''),
passportNumber: td1.slice(5, 14).replace(/</g, ''),
dateOfBirth: td1.slice(30, 36),
dateOfExpiry: td1.slice(38, 44),
};
}
🤖 Prompt for AI Agents
In packages/mobile-sdk-alpha/src/processing/mrz.ts around lines 177 to 190, the
function concatenates line1 + line2 which can produce the string "undefined" if
lines[1] is missing; normalize inputs by defaulting line1 and line2 to empty
strings (e.g., const line1 = lines[0] || '' and const line2 = lines[1] || ''),
trim them, and use the concatenated safe string for slicing; also guard against
too-short MRZ by ensuring the concatenated string has the expected minimum
length before slicing (or pad with '<') to avoid producing incorrect fields.


/**
* Validate all check digits for TD1 MRZ format
*/
function validateTD1CheckDigits(lines: string[]): Omit<MRZValidation, 'format' | 'overall'> {
const line1 = lines[0];
const line2 = lines[1];
const concatenatedLines = line1 + line2;

const documentNumber = concatenatedLines.slice(5, 14);
const documentNumberCheckDigit = concatenatedLines.slice(14, 15);
const dateOfBirth = concatenatedLines.slice(30, 36);
const dobCheckDigit = concatenatedLines.slice(36, 37);
const dateOfExpiry = concatenatedLines.slice(38, 44);
const expiryCheckDigit = concatenatedLines.slice(44, 45);

return {
passportNumberChecksum: verifyCheckDigit(documentNumber, documentNumberCheckDigit),
dateOfBirthChecksum: verifyCheckDigit(dateOfBirth, dobCheckDigit),
dateOfExpiryChecksum: verifyCheckDigit(dateOfExpiry, expiryCheckDigit),
compositeChecksum: true, // TD1 doesn't have a composite check digit like TD3
};
}
Comment on lines +131 to +152
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

TD1 validation must compute the overall (composite) check digit; hardcoding true is unsafe.

Per ICAO 9303 TD1, the last character of line 2 is the overall check digit for the upper and middle lines. Compute it over line1 + line2[0..28] instead of returning true. This materially impacts validation correctness. (doubango.org)

Apply this diff:

-function validateTD1CheckDigits(lines: string[]): Omit<MRZValidation, 'format' | 'overall'> {
-  const line1 = lines[0];
-  const line2 = lines[1];
-  const concatenatedLines = line1 + line2;
-
-  const documentNumber = concatenatedLines.slice(5, 14);
-  const documentNumberCheckDigit = concatenatedLines.slice(14, 15);
-  const dateOfBirth = concatenatedLines.slice(30, 36);
-  const dobCheckDigit = concatenatedLines.slice(36, 37);
-  const dateOfExpiry = concatenatedLines.slice(38, 44);
-  const expiryCheckDigit = concatenatedLines.slice(44, 45);
-
-  return {
-    passportNumberChecksum: verifyCheckDigit(documentNumber, documentNumberCheckDigit),
-    dateOfBirthChecksum: verifyCheckDigit(dateOfBirth, dobCheckDigit),
-    dateOfExpiryChecksum: verifyCheckDigit(dateOfExpiry, expiryCheckDigit),
-    compositeChecksum: true, // TD1 doesn't have a composite check digit like TD3
-  };
-}
+function validateTD1CheckDigits(lines: string[]): Omit<MRZValidation, 'format' | 'overall'> {
+  // Build safe TD1 buffer and address fields using per-line offsets
+  const td1 = lines.slice(0, 3).filter(Boolean).join('');
+  const line1 = td1.slice(0, 30);
+  const line2 = td1.slice(30, 60);
+
+  const documentNumber = line1.slice(5, 14);
+  const documentNumberCheckDigit = line1.slice(14, 15);
+  const dateOfBirth = line2.slice(0, 6);
+  const dobCheckDigit = line2.slice(6, 7);
+  const dateOfExpiry = line2.slice(8, 14);
+  const expiryCheckDigit = line2.slice(14, 15);
+
+  // TD1 overall check digit is the last char of line 2; compute over line1 + line2[0..28]
+  const compositeField = line1 + line2.slice(0, 29);
+  const compositeCheckDigit = line2.slice(29, 30);
+
+  const haveTwoFullLines = line1.length === 30 && line2.length === 30;
+  return {
+    passportNumberChecksum: verifyCheckDigit(documentNumber, documentNumberCheckDigit),
+    dateOfBirthChecksum: verifyCheckDigit(dateOfBirth, dobCheckDigit),
+    dateOfExpiryChecksum: verifyCheckDigit(dateOfExpiry, expiryCheckDigit),
+    compositeChecksum: haveTwoFullLines && verifyCheckDigit(compositeField, compositeCheckDigit),
+  };
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* Validate all check digits for TD1 MRZ format
*/
function validateTD1CheckDigits(lines: string[]): Omit<MRZValidation, 'format' | 'overall'> {
const line1 = lines[0];
const line2 = lines[1];
const concatenatedLines = line1 + line2;
const documentNumber = concatenatedLines.slice(5, 14);
const documentNumberCheckDigit = concatenatedLines.slice(14, 15);
const dateOfBirth = concatenatedLines.slice(30, 36);
const dobCheckDigit = concatenatedLines.slice(36, 37);
const dateOfExpiry = concatenatedLines.slice(38, 44);
const expiryCheckDigit = concatenatedLines.slice(44, 45);
return {
passportNumberChecksum: verifyCheckDigit(documentNumber, documentNumberCheckDigit),
dateOfBirthChecksum: verifyCheckDigit(dateOfBirth, dobCheckDigit),
dateOfExpiryChecksum: verifyCheckDigit(dateOfExpiry, expiryCheckDigit),
compositeChecksum: true, // TD1 doesn't have a composite check digit like TD3
};
}
/**
* Validate all check digits for TD1 MRZ format
*/
function validateTD1CheckDigits(lines: string[]): Omit<MRZValidation, 'format' | 'overall'> {
// Build safe TD1 buffer and address fields using per-line offsets
const td1 = lines.slice(0, 3).filter(Boolean).join('');
const line1 = td1.slice(0, 30);
const line2 = td1.slice(30, 60);
const documentNumber = line1.slice(5, 14);
const documentNumberCheckDigit = line1.slice(14, 15);
const dateOfBirth = line2.slice(0, 6);
const dobCheckDigit = line2.slice(6, 7);
const dateOfExpiry = line2.slice(8, 14);
const expiryCheckDigit = line2.slice(14, 15);
// TD1 overall check digit is the last char of line 2; compute over line1 + line2[0..28]
const compositeField = line1 + line2.slice(0, 29);
const compositeCheckDigit = line2.slice(29, 30);
const haveTwoFullLines = line1.length === 30 && line2.length === 30;
return {
passportNumberChecksum: verifyCheckDigit(documentNumber, documentNumberCheckDigit),
dateOfBirthChecksum: verifyCheckDigit(dateOfBirth, dobCheckDigit),
dateOfExpiryChecksum: verifyCheckDigit(dateOfExpiry, expiryCheckDigit),
compositeChecksum: haveTwoFullLines && verifyCheckDigit(compositeField, compositeCheckDigit),
};
}
🤖 Prompt for AI Agents
In packages/mobile-sdk-alpha/src/processing/mrz.ts around lines 131-152, the
compositeChecksum is incorrectly hardcoded to true; replace it with a real
verification by computing the overall check digit over line1 plus the first 29
characters of line2 (i.e., line1 + line2.slice(0, 29)) and compare that string
against the last character of line2 (line2.slice(29, 30)) using
verifyCheckDigit; return the boolean result as compositeChecksum instead of
true.


/**
* Validate all check digits for TD3 MRZ
* TD3 Line 2 format: PASSPORT(9)CHECK(1)NATIONALITY(3)DOB(6)DOBCHECK(1)SEX(1)EXPIRY(6)EXPIRYCHECK(1)PERSONAL(14)PERSONALCHECK(1)FINALCHECK(1)
Expand Down Expand Up @@ -215,25 +256,36 @@

// Validate format
const isValidTD3 = validateTD3Format(lines);
const isValidTD1 = validateTD1Format(lines);

if (!isValidTD3) {
if (!isValidTD3 && !isValidTD1) {
throw new MrzParseError(
`Invalid MRZ format: Expected TD3 format (2 lines × 44 characters), got ${lines.length} lines with lengths [${lines.map(l => l.length).join(', ')}]`,
`Invalid MRZ format: Expected TD3 or TD1 format, got ${lines.length} lines with lengths [${lines.map(l => l.length).join(', ')}]`,
);
}

// Extract basic information
const info = extractTD3Info(lines);

// Validate check digits
const checksums = validateTD3CheckDigits(lines);

// Create validation result
const validation: MRZValidation = {
format: isValidTD3,
...checksums,
overall: isValidTD3 && Object.values(checksums).every(Boolean),
};
let info: Omit<MRZInfo, 'validation'>;
let checksums: Omit<MRZValidation, 'format' | 'overall'>;
let validation: MRZValidation;

if (isValidTD3) {
// Extract basic information
info = extractTD3Info(lines);
checksums = validateTD3CheckDigits(lines);
validation = {
format: isValidTD3,
...checksums,
overall: isValidTD3 && Object.values(checksums).every(Boolean),
};
} else {
info = extractTD1Info(lines);
checksums = validateTD1CheckDigits(lines);
validation = {
format: isValidTD1,
...checksums,
overall: isValidTD1 && Object.values(checksums).every(Boolean),
};
}

return {
...info,
Expand Down
4 changes: 0 additions & 4 deletions packages/mobile-sdk-alpha/src/types/public.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,6 @@ export interface MRZInfo {
passportNumber: string;
dateOfBirth: string;
dateOfExpiry: string;
surname: string;
givenNames: string;
sex: string;
nationality: string;
issuingCountry: string;
documentType: string;
validation: MRZValidation;
Expand Down
26 changes: 24 additions & 2 deletions packages/mobile-sdk-alpha/tests/processing/mrz.test.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,40 @@
import { describe, expect, it } from 'vitest';

import { formatDateToYYMMDD, MrzParseError } from '../../src';
import { extractMRZInfo } from '../../src/mrz';
import { MrzParseError } from '../../src/errors';
import { extractMRZInfo, formatDateToYYMMDD } from '../../src/processing/mrz';

const sample = `P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<
L898902C36UTO7408122F1204159ZE184226B<<<<<10`;

const sampleTD1 = `IDFRAX4RTBPFW46<<<<<<<<<<<<<<<9007138M3002119ESP6DUMMY<<DUMMY<<<<<<<<<<<<<<<<<<`;

Comment on lines +9 to +10
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add a 3-line TD1 sample to harden coverage against real-world scanners.

Most TD1 sources emit 3 lines of 30 chars; current tests only cover a single-line concatenation. Add a case with explicit newlines to prevent regressions in splitting/joining.

Apply this diff to extend coverage:

 const sampleTD1 = `IDFRAX4RTBPFW46<<<<<<<<<<<<<<<9007138M3002119ESP6DUMMY<<DUMMY<<<<<<<<<<<<<<<<<<`;
+
+const sampleTD1ThreeLines =
+  `IDFRAX4RTBPFW46<<<<<<<<<<<<<<<\n` +
+  `9007138M3002119ESP6DUMMY<<DUM\n` +
+  `MY<<<<<<<<<<<<<<<<<<`;

And a test:

   it('parses valid TD1 MRZ', () => {
     const info = extractMRZInfo(sampleTD1);
@@
     expect(info.validation.overall).toBe(true);
   });
+
+  it('parses valid TD1 MRZ split across 3 lines', () => {
+    const info = extractMRZInfo(sampleTD1ThreeLines);
+    expect(info.passportNumber).toBe('X4RTBPFW4');
+    expect(info.issuingCountry).toBe('FRA');
+    expect(info.dateOfBirth).toBe('900713');
+    expect(info.dateOfExpiry).toBe('300211');
+    expect(info.validation.overall).toBe(true);
+  });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const sampleTD1 = `IDFRAX4RTBPFW46<<<<<<<<<<<<<<<9007138M3002119ESP6DUMMY<<DUMMY<<<<<<<<<<<<<<<<<<`;
const sampleTD1 = `IDFRAX4RTBPFW46<<<<<<<<<<<<<<<9007138M3002119ESP6DUMMY<<DUMMY<<<<<<<<<<<<<<<<<<`;
const sampleTD1ThreeLines =
`IDFRAX4RTBPFW46<<<<<<<<<<<<<<<\n` +
`9007138M3002119ESP6DUMMY<<DUM\n` +
`MY<<<<<<<<<<<<<<<<<<`;
describe('extractMRZInfo', () => {
it('parses valid TD1 MRZ', () => {
const info = extractMRZInfo(sampleTD1);
expect(info.validation.overall).toBe(true);
});
it('parses valid TD1 MRZ split across 3 lines', () => {
const info = extractMRZInfo(sampleTD1ThreeLines);
expect(info.passportNumber).toBe('X4RTBPFW4');
expect(info.issuingCountry).toBe('FRA');
expect(info.dateOfBirth).toBe('900713');
expect(info.dateOfExpiry).toBe('300211');
expect(info.validation.overall).toBe(true);
});
});
🤖 Prompt for AI Agents
In packages/mobile-sdk-alpha/tests/processing/mrz.test.ts around lines 9-10, the
test only defines a single-line TD1 sample; add a new 3-line TD1 sample constant
(three lines of 30 characters each separated by explicit "\n" newlines) and a
corresponding test case that feeds the multiline sample into the MRZ processing
functions to verify correct splitting/joining and parsing behavior; ensure the
new test asserts that the parser returns the same normalized/concatenated MRZ
string and expected fields as the single-line case to harden coverage against
real-world TD1 scanners.

describe('extractMRZInfo', () => {
it('parses valid TD3 MRZ', () => {
const info = extractMRZInfo(sample);
expect(info.passportNumber).toBe('L898902C3');
expect(info.validation.overall).toBe(true);
});

it('parses valid TD1 MRZ', () => {
const info = extractMRZInfo(sampleTD1);
expect(info.passportNumber).toBe('X4RTBPFW4');
expect(info.issuingCountry).toBe('FRA');
expect(info.dateOfBirth).toBe('900713');
expect(info.dateOfExpiry).toBe('300211');
expect(info.validation.overall).toBe(true);
});

it('rejects invalid TD1 MRZ', () => {
const invalid = `FRAX4RTBPFW46`;
expect(() => extractMRZInfo(invalid)).toThrow();
});

it('Fails overall validation for invalid TD1 MRZ', () => {
const invalid = `IDFRAX4RTBPFW46`;
const info = extractMRZInfo(invalid);
expect(info.validation.overall).toBe(false);
});

it('rejects malformed MRZ', () => {
const invalid = 'P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<';
expect(() => extractMRZInfo(invalid)).toThrowError(MrzParseError);
Expand Down
Loading