Skip to content

Instantly share code, notes, and snippets.

@RobJohnston
Created September 28, 2017 17:29
Show Gist options
  • Save RobJohnston/60b49b0303f6557cfc0789a426b2155a to your computer and use it in GitHub Desktop.
Save RobJohnston/60b49b0303f6557cfc0789a426b2155a to your computer and use it in GitHub Desktop.
jQuery Validation method for the Machine Readable Zone of a Machine Readable Travel Document.
/*
Validate the Machine Readable Zone (MRZ) of any Machine Readable Travel Document (MRTD) conforming with the
specifications of ICAO Doc 9303-4.
This method can validate:
* a Machine Readable Passports (MRP) and other TD3 sized MRTDs;
* a Machine Readable Official Travel Document (MROTD); and
* a Machine Readable Visa (MRV).
This method does not validate:
* if a date (birthdate or expiry date) is valid.
* if a ISO 3166-1 alpha-3 code (issuing state or nationality) is officially assigned.
References:
https://www.icao.int/publications/pages/publication.aspx?docnum=9303
https://en.wikipedia.org/wiki/Machine-readable_passport
*/
$.validator.addMethod( "mrtd", function( value, element ) {
if ( this.optional( element ) ) {
return true;
}
//Remove carriage return and newline characters from value.
value = value.replace( /([\r\n])+/g, "" );
//Quick test of valid characters and length.
if ( !/^([A-Z0-9<]){72,90}$/g.test( value ) ) {
return false;
}
var re,
match,
documentType;
//Determine the type of travel document based on the length of the input value and its first character.
//Each document type has its own regex.
switch ( value.length ) {
case 72:
//Official travel document or visa (2 lines of 36 characters each).
switch ( value.charAt( 0 ) ) {
case "A":
case "C":
case "I":
//Size 2 official travel document.
documentType = "TD2";
/*
[ACI] #A, C, or I to designate the particular type of document
[A-UW-Z<]{1} #Type (V shall not be used)
[A-Z<]{3} #Issuing state
[A-Z<]{31} #Name
([0-9A-Z<]{9}) #Document number
([0-9]{1}) #Check digit for document number
[A-Z<]{3} #Nationality
([0-9<]{6}) #Date of birth (YYMMDD)
([0-9]{1}) #Check digit for date of birth
[MF<] #Sex
([0-9]{6}) #Expiration date (YYMMDD)
([0-9]{1}) #Check digit for expiration date
([0-9A-Z<]{7}) #Optional
([0-9]{1}) #Composite check digit to verify the data elements of the lower machine readable line.
*/
re = /^[ACI][A-UW-Z<]{1}[A-Z<]{3}[A-Z<]{31}([0-9A-Z<]{9})([0-9]{1})[A-Z<]{3}([0-9<]{6})([0-9]{1})[MF<]([0-9]{6})([0-9]{1})[0-9A-Z<]{7}([0-9]{1})$/g;
break;
case "V":
//Format-B visa.
documentType = "MRV-B";
/*
V #V, indicating a visa
[A-Z<]{1} #Type of visa
[A-Z<]{3} #Issuing state
[A-Z<]{31} #Name
([0-9A-Z<]{9}) #Passport or visa number
([0-9]{1}) #Check digit for passport or visa number
[A-Z<]{3} #Nationality
([0-9<]{6}) #Date of birth (YYMMDD)
([0-9]{1}) #Check digit for date of birth
[MF<] #Sex
([0-9]{6}) #Expiration date (YYMMDD)
([0-9]{1}) #Check digit for expiration date
([0-9A-Z<]{8}) #Optional data elements
*/
re = /^V[A-Z<]{1}[A-Z<]{3}[A-Z<]{31}([0-9A-Z<]{9})([0-9]{1})[A-Z<]{3}([0-9<]{6})([0-9]{1})[MF<]([0-9]{6})([0-9]{1})([0-9A-Z<]{8})$/g;
break;
default:
return false;
}
break;
case 88:
//Passport or visa (2 lines of 44 characters each).
switch ( value.charAt( 0 ) ) {
case "P":
//Size 3 travel document (passport).
documentType = "TD3";
/*
P #P, indicating a passport
[A-Z<]{1} #Type (for countries that distinguish between different types of passports)
[A-Z<]{3} #Issuing state
[A-Z<]{39} #Name
([0-9A-Z<]{9}) #Passport number
([0-9]{1}) #Check digit for passport number
[A-Z<]{3} #Nationality
([0-9<]{6}) #Date of birth (YYMMDD)
([0-9]{1}) #Check digit for date of birth
[MF<] #Sex
([0-9]{6}) #Expiration date (YYMMDD)
([0-9]{1}) #Check digit for expiration date
([0-9A-Z<]{14}) #Personal number (may be used by the issuing country as it desires)
([0-9<]{1}) #Check digit for personal number (may be < if all characters are <)
([0-9]{1}) #Composite check digit for everything from passport number to end, excluding nationality and sex.
*/
re = /^P[A-Z<]{1}[A-Z<]{3}[A-Z<]{39}([0-9A-Z<]{9})([0-9]{1})[A-Z<]{3}([0-9<]{6})([0-9]{1})[MF<]([0-9]{6})([0-9]{1})([0-9A-Z<]{14})([0-9<]{1})([0-9]{1})$/g;
break;
case "V":
//Format-A visa
documentType = "MRV-A";
/*
V #V, indicating a visa
[A-Z<]{1} #Type of visa
[A-Z<]{3} #Issuing state
[A-Z<]{39} #Name
([0-9A-Z<]{9}) #Passport or visa number
([0-9]{1}) #Check digit for passport or visa number
[A-Z<]{3} #Nationality
([0-9<]{6}) #Date of birth (YYMMDD)
([0-9]{1}) #Check digit for date of birth
[MF<] #Sex
([0-9]{6}) #Expiration date (YYMMDD)
([0-9]{1}) #Check digit for expiration date
([0-9A-Z<]{16}) #Optional data elements
*/
re = /^V[A-Z<]{1}[A-Z<]{3}[A-Z<]{39}([0-9A-Z<]{9})([0-9]{1})[A-Z<]{3}([0-9<]{6})([0-9]{1})[MF<]([0-9]{6})([0-9]{1})([0-9A-Z<]{16})$/g;
break;
default:
return false;
}
break;
case 90:
//Size 1 official travel document (3 lines of 30 characters each).
documentType = "TD1";
/*
[ACI] #A, C, or I to designate the particular type of document
[A-UW-Z<]{1} #Type (V shall not be used)
[A-Z<]{3} #Issuing state
([0-9A-Z<]{9}) #Document number
([0-9]{1}) #Check digit for document number
([0-9A-Z<]{15}) #Optional data elements
([0-9<]{6}) #Date of birth (YYMMDD)
([0-9]{1}) #Check digit for date of birth
[MF<] #Sex
([0-9]{6}) #Expiration date (YYMMDD)
([0-9]{1}) #Check digit for expiration date
[A-Z<]{3} #Nationality
([0-9A-Z<]{11}) #Optional
([0-9]{1}) #Composite check digit to verify the data elements of the lower machine readable line.
[A-Z<]{30} #Name
*/
re = /^[ACI][A-UW-Z<]{1}[A-Z<]{3}([0-9A-Z<]{9})([0-9]{1})([0-9A-Z<]{15})([0-9<]{6})([0-9]{1})[MF<]([0-9]{6})([0-9]{1})[A-Z<]{3}([0-9A-Z<]{11})([0-9]{1})[A-Z<]{30}$/g;
break;
default:
return false;
}
console.log( documentType );
//Use the regex to search the input value.
match = re.exec( value );
if ( match == null ) {
return false;
}
//Test the individual check digits.
var documentNumber = match[1],
documentNumberCheckDigit = match[2],
dateOfBirth,
dateOfBirthCheckDigit,
expiration,
expirationCheckDigit,
composite,
compositeCheckDigit;
switch ( documentType ) {
case "TD1":
var optional1, optional2;
optional1 = match[3],
dateOfBirth = match[4],
dateOfBirthCheckDigit = match[5],
expiration = match[6],
expirationCheckDigit = match[7],
optional2 = match[8],
composite = documentNumber + documentNumberCheckDigit + optional1 + dateOfBirth + dateOfBirthCheckDigit + expiration + expirationCheckDigit + optional2,
compositeCheckDigit = match[9];
if ( check( documentNumber, documentNumberCheckDigit ) ) {
if ( check( dateOfBirth, dateOfBirthCheckDigit ) ) {
if ( check( expiration, expirationCheckDigit ) ) {
if ( check( composite, compositeCheckDigit ) ) {
return true;
}
}
}
}
break;
case "TD2":
dateOfBirth = match[3],
dateOfBirthCheckDigit = match[4],
expiration = match[5],
expirationCheckDigit = match[6],
composite = documentNumber + documentNumberCheckDigit + dateOfBirth + dateOfBirthCheckDigit + expiration + expirationCheckDigit,
compositeCheckDigit = match[7];
if ( check( documentNumber, documentNumberCheckDigit ) ) {
if ( check( dateOfBirth, dateOfBirthCheckDigit ) ) {
if ( check( expiration, expirationCheckDigit ) ) {
if ( check( composite, compositeCheckDigit ) ) {
return true;
}
}
}
}
break;
case "TD3":
var personalNumber, personalNumberCheckDigit;
dateOfBirth = match[3],
dateOfBirthCheckDigit = match[4],
expiration = match[5],
expirationCheckDigit = match[6],
personalNumber = match[7],
personalNumberCheckDigit = match[8],
composite = documentNumber + documentNumberCheckDigit + dateOfBirth + dateOfBirthCheckDigit + expiration + expirationCheckDigit + personalNumber + personalNumberCheckDigit,
compositeCheckDigit = match[9];
if ( check( documentNumber, documentNumberCheckDigit ) ) {
if ( check( dateOfBirth, dateOfBirthCheckDigit ) ) {
if ( check( expiration, expirationCheckDigit ) ) {
//Result of next check could be zero or the filler character ("<") if the personal number field is not used.
if ( check( personalNumber, personalNumberCheckDigit ) || personalNumber == '<'.repeat( 14 ) && personalNumberCheckDigit == '<' ) {
if ( check( composite, compositeCheckDigit ) ) {
return true;
}
}
}
}
}
break;
case "MRV-A":
case "MRV-B":
dateOfBirth = match[3],
dateOfBirthCheckDigit = match[4],
expiration = match[5],
expirationCheckDigit = match[6];
if ( check( documentNumber, documentNumberCheckDigit ) ) {
if ( check( dateOfBirth, dateOfBirthCheckDigit ) ) {
if ( check( expiration, expirationCheckDigit ) ) {
return true;
}
}
}
break;
default:
return false;
}
return false;
/**
* Perform a check digit calculation to verify the data in the MRZ.
* input - The data element to check.
* digit - The check digit expected.
* Returns true if the result of the check of the "input" matches the "digit".
*/
function check( input, digit ) {
var a = input.split( '' ),
c = '',
position = 0,
remainder = 0,
sum = 0,
value = 0,
weight = 0;
for ( var i = 0; i < a.length; i++ ) {
c = a[i];
position++;
//The weight of the first position is 7, of the second it is 3, and of the third it is 1.
if ( position == 4 )
position = 1;
switch ( position ) {
case 1:
weight = 7;
break;
case 2:
weight = 3;
break;
case 3:
weight = 1;
break;
}
//Each character of input has a value. For the digits 0 to 9 it is the value of the digits,
//for the letters A to Z it is 10 to 35, and for the filler "<" it is 0.
if ( isNaN( parseInt( c, 10 ) ) ) {
if ( c == '<' )
value = 0;
else
value = c.charCodeAt( 0 ) - 55;
}
else {
value = c;
}
//Sum the result of multiplying the value by the weight.
sum = sum + ( value * weight );
}
//The remainder of the final sum divided by 10 should be the check digit.
remainder = ( sum % 10 );
return remainder == digit;
}
}, "Please enter a valid machine-readable travel document.");
@RobJohnston
Copy link
Author

QUnit.test( "mrtd", function( assert ) {
var method = methodTest( "mrtd" );
assert.ok( method( "P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L898902C36UTO7408122F1204159ZE184226B<<<<<10" ), "Valid passport" );
assert.ok( method( "P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L898902C36UTO7408122<1204159ZE184226B<<<<<10" ), "Valid passport: Unspecified sex" );
assert.ok( method( "P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L898902C36UTO7408122F1204159<<<<<<<<<<<<<<00" ), "Valid passport: No personal number and check digit '0'" );
assert.ok( method( "P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L898902C36UTO7408122F1204159<<<<<<<<<<<<<<<0" ), "Valid passport: No personal number and check digit '<'" );
assert.ok( method( "P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L898902C36UTO74<<<<1F1204159ZE184226B<<<<<10" ), "Valid passport: Birth date not fully known" );
assert.ok( method( "P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L898902C36UTO7408122<1204159ZE184226B<<<<<10" ), "Valid passport: Unspecified gender" );
assert.ok( !method( "V<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L898902C36UTO7408122F1204159ZE184226B<<<<<10" ), "Invalid passport: Does not start with 'P'" );
assert.ok( !method( "P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L898902C36UTO7408122F1204159ZE184226B<<<<10" ), "Invalid passport: Too short" );
assert.ok( !method( "P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L898902C36UTO7408122F1204159ZE184226B<<<<<<10" ), "Invalid passport: Too long" );
assert.ok( !method( "P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L898902C35UTO7408122F1204159ZE184226B<<<<<10" ), "Invalid passport: Check digit for document number" );
assert.ok( !method( "P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L898902C36UTO7408121F1204159ZE184226B<<<<<10" ), "Invalid passport: Check digit for birth date" );
assert.ok( !method( "P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L898902C36UTO7408122X1204159ZE184226B<<<<<10" ), "Invalid passport: Sex not 'M', 'F', or '<'" );
assert.ok( !method( "P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L898902C36UTO7408122F1204158ZE184226B<<<<<10" ), "Invalid passport: Check digit for expiration date" );
assert.ok( !method( "P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L898902C36UTO7408122F1204159ZE184226B<<<<<00" ), "Invalid passport: Check digit for personal number" );
assert.ok( !method( "P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L898902C36UTO7408122F1204159ZE184226B<<<<<19" ), "Invalid passport: Composite check digit" );

assert.ok( method( "V<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L8988901C4XXX4009078F96121096ZE184226B<<<<<<" ), "Valid format-A visa" );
assert.ok( method( "V<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L8988901C4XXX40<<<<8F96121096ZE184226B<<<<<<" ), "Valid format-A visa: Birth date not fully known" );
assert.ok( method( "V<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L8988901C4XXX4009078<96121096ZE184226B<<<<<<" ), "Valid format-A visa: Unspecified sex" );
assert.ok( !method( "P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L8988901C4XXX4009078F96121096ZE184226B<<<<<<" ), "Invalid format-A visa: Does not start with 'V'" );
assert.ok( !method( "V<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L8988901C4XXX4009078F96121096ZE184226B<<<<<" ), "Invalid format-A visa: Too short" );
assert.ok( !method( "V<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L8988901C4XXX4009078F96121096ZE184226B<<<<<<<" ), "Invalid format-A visa: Too long" );
assert.ok( !method( "V<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L8988901C3XXX4009078F96121096ZE184226B<<<<<<" ), "Invalid format-A visa: Check digit for visa number" );
assert.ok( !method( "V<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L8988901C4XXX4009077F96121096ZE184226B<<<<<<" ), "Invalid format-A visa: Check digit for birth date" );
assert.ok( !method( "V<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L8988901C4XXX4009078X96121096ZE184226B<<<<<<" ), "Invalid format-A visa: Sex not 'M', 'F', or '<'" );
assert.ok( !method( "V<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<L8988901C4XXX4009078F96121086ZE184226B<<<<<<" ), "Invalid format-A visa: Check digit for expiration date" );

assert.ok( method( "V<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<L8988901C4XXX4009078F96121096ZE18422" ), "Valid format-B visa" );
assert.ok( method( "V<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<L8988901C4XXX40<<<<8F96121096ZE18422" ), "Valid format-B visa: Birth date not fully known" );
assert.ok( method( "V<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<L8988901C4XXX4009078<96121096ZE18422" ), "Valid format-B visa: Unspecified sex" );
assert.ok( !method( "P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<L8988901C4XXX4009078F9612109<<<<<<<<" ), "Invalid format-B visa: Does not start with 'V'" );
assert.ok( !method( "V<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<L8988901C4XXX4009078F9612109<<<<<<<" ), "Invalid format-B visa: Too short" );
assert.ok( !method( "V<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<L8988901C4XXX4009078F9612109<<<<<<<<<" ), "Invalid format-B visa: Too long" );
assert.ok( !method( "V<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<L8988901C3XXX4009078F9612109<<<<<<<<" ), "Invalid format-B visa: Check digit for visa number" );
assert.ok( !method( "V<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<L8988901C4XXX4009077F9612109<<<<<<<<" ), "Invalid format-B visa: Check digit for birth date" );
assert.ok( !method( "V<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<L8988901C4XXX4009078X9612109<<<<<<<<" ), "Invalid format-B visa: Sex not 'M', 'F', or '<'" );
assert.ok( !method( "V<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<L8988901C4XXX4009078F9612108<<<<<<<<" ), "Invalid format-B visa: Check digit for expiration date" );

assert.ok( method( "A<UTOD231458907<<<<<<<<<<<<<<<7408122F1204159UTO<<<<<<<<<<<6ERIKSSON<<ANNA<MARIA<<<<<<<<<<" ), "Valid size 1 official travel document: 'A' type" );
assert.ok( method( "C<UTOD231458907<<<<<<<<<<<<<<<7408122F1204159UTO<<<<<<<<<<<6ERIKSSON<<ANNA<MARIA<<<<<<<<<<" ), "Valid size 1 official travel document: 'C' type" );
assert.ok( method( "I<UTOD231458907<<<<<<<<<<<<<<<7408122F1204159UTO<<<<<<<<<<<6ERIKSSON<<ANNA<MARIA<<<<<<<<<<" ), "Valid size 1 official travel document: 'I' type" );
assert.ok( method( "I<UTOD231458907<<<<<<<<<<<<<<<74<<<<1F1204159UTO<<<<<<<<<<<6ERIKSSON<<ANNA<MARIA<<<<<<<<<<" ), "Valid size 1 official travel document: Birth date not fully known" );
assert.ok( method( "I<UTOD231458907<<<<<<<<<<<<<<<7408122<1204159UTO<<<<<<<<<<<6ERIKSSON<<ANNA<MARIA<<<<<<<<<<" ), "Valid size 1 official travel document: Unspecified sex" );
assert.ok( !method( "V<UTOD231458907<<<<<<<<<<<<<<<7408122F1204159UTO<<<<<<<<<<<6ERIKSSON<<ANNA<MARIA<<<<<<<<<<" ), "Invalid size 1 official travel document: Does not begin with 'A', 'C', or 'I'" );
assert.ok( !method( "A<UTOD231458907<<<<<<<<<<<<<<<7408122F1204159UTO<<<<<<<<<<<6ERIKSSON<<ANNA<MARIA<<<<<<<<<" ), "Invalid size 1 official travel document: Too short" );
assert.ok( !method( "A<UTOD231458907<<<<<<<<<<<<<<<7408122F1204159UTO<<<<<<<<<<<6ERIKSSON<<ANNA<MARIA<<<<<<<<<<<" ), "Invalid size 1 official travel document: Too long" );
assert.ok( !method( "IVUTOD231458907<<<<<<<<<<<<<<<7408122F1204159UTO<<<<<<<<<<<6ERIKSSON<<ANNA<MARIA<<<<<<<<<<" ), "Invalid size 1 official travel document: 'V' cannot be used in document type" );
assert.ok( !method( "I<UTOD231458906<<<<<<<<<<<<<<<7408122F1204159UTO<<<<<<<<<<<6ERIKSSON<<ANNA<MARIA<<<<<<<<<<" ), "Invalid size 1 official travel document: Check digit for document number" );
assert.ok( !method( "I<UTOD231458907<<<<<<<<<<<<<<<7408121F1204159UTO<<<<<<<<<<<6ERIKSSON<<ANNA<MARIA<<<<<<<<<<" ), "Invalid size 1 official travel document: Check digit for birth date" );
assert.ok( !method( "I<UTOD231458907<<<<<<<<<<<<<<<7408122X1204159UTO<<<<<<<<<<<6ERIKSSON<<ANNA<MARIA<<<<<<<<<<" ), "Invalid size 1 official travel document: Sex not 'M', 'F', or '<'" );
assert.ok( !method( "I<UTOD231458907<<<<<<<<<<<<<<<7408122F1204158UTO<<<<<<<<<<<6ERIKSSON<<ANNA<MARIA<<<<<<<<<<" ), "Invalid size 1 official travel document: Check digit for expiration date" );
assert.ok( !method( "I<UTOD231458907<<<<<<<<<<<<<<<7408122F1204159UTO<<<<<<<<<<<5ERIKSSON<<ANNA<MARIA<<<<<<<<<<" ), "Invalid size 1 official travel document: Composite check digit" );

assert.ok( method( "A<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<D231458907UTO7408122F1204159<<<<<<<6" ), "Valid size 2 official travel document: 'A' type" );
assert.ok( method( "C<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<D231458907UTO7408122F1204159<<<<<<<6" ), "Valid size 2 official travel document: 'C' type" );
assert.ok( method( "I<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<D231458907UTO7408122F1204159<<<<<<<6" ), "Valid size 2 official travel document: 'I' type" );
assert.ok( method( "I<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<D231458907UTO74<<<<1F1204159<<<<<<<6" ), "Valid size 2 official travel document: Birth date not fully known" );
assert.ok( method( "I<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<D231458907UTO7408122<1204159<<<<<<<6" ), "Valid size 2 official travel document: Unspecified sex" );
assert.ok( !method( "V<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<D231458907UTO7408122F1204159<<<<<<<6" ), "Invalid size 2 official travel document: Does not begin with 'A', 'C', or 'I'" );
assert.ok( !method( "I<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<D231458907UTO7408122F1204159<<<<<<6" ), "Invalid size 2 official travel document: Too short" );
assert.ok( !method( "I<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<D231458907UTO7408122F1204159<<<<<<<<6" ), "Invalid size 2 official travel document: Too long" );
assert.ok( !method( "IVUTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<D231458907UTO7408122F1204159<<<<<<<6" ), "Invalid size 2 official travel document: 'V' cannot be used in document type" );
assert.ok( !method( "I<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<D231458906UTO7408122F1204159<<<<<<<6" ), "Invalid size 2 official travel document: Check digit for document number" );
assert.ok( !method( "I<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<D231458907UTO7408121F1204159<<<<<<<6" ), "Invalid size 2 official travel document: Check digit for birth date" );
assert.ok( !method( "I<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<D231458907UTO7408122X1204159<<<<<<<6" ), "Invalid size 2 official travel document: Sex not 'M', 'F', or '<'" );
assert.ok( !method( "I<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<D231458907UTO7408122F1204158<<<<<<<6" ), "Invalid size 2 official travel document: Check digit for expiration date" );
assert.ok( !method( "I<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<D231458907UTO7408122F1204159<<<<<<<5" ), "Invalid size 2 official travel document: Composite check digit" );
} );

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment