From c19b85c463abd796fe0d919bd99074f2d880df0f Mon Sep 17 00:00:00 2001 From: Cristin O'Connor Date: Thu, 1 Aug 2024 11:09:28 -0600 Subject: [PATCH] POS-1253 Create new validator for both Canadian and U.S. routing numbers --- .tool-versions | 3 +- __tests__/validation.test.js | 80 ++++++++++++++++++++++++++++++++++++ package.json | 2 +- src/index.js | 1 + src/validation.js | 22 ++++++++++ 5 files changed, 106 insertions(+), 2 deletions(-) diff --git a/.tool-versions b/.tool-versions index 15af05d2..e3c26d5b 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1,2 @@ -yarn 1.22.19 \ No newline at end of file +yarn 1.22.19 +nodejs 20.17.0 \ No newline at end of file diff --git a/__tests__/validation.test.js b/__tests__/validation.test.js index 5ccfad82..e3d928e8 100644 --- a/__tests__/validation.test.js +++ b/__tests__/validation.test.js @@ -42,6 +42,9 @@ import { isRoutingNumber, IS_ROUTING_NUMBER, IS_ROUTING_NUMBER_ERROR, + isAmericanOrCanadianRoutingNumber, + IS_AMERICAN_OR_CANADIAN_ROUTING_NUMBER, + IS_AMERICAN_OR_CANADIAN_ROUTING_NUMBER_ERROR, validateWhen, VALIDATE_WHEN, VALIDATE_WHEN_ERROR, @@ -159,6 +162,18 @@ test('isRoutingNumber validator produces correct validator object', () => { }); }); +test('isAmericanOrCanadianRoutingNumber validator produces correct validator object', () => { + expect( + isAmericanOrCanadianRoutingNumber.error).toBe( + IS_AMERICAN_OR_CANADIAN_ROUTING_NUMBER_ERROR + ); + expect(isAmericanOrCanadianRoutingNumber()).toEqual({ + type: IS_AMERICAN_OR_CANADIAN_ROUTING_NUMBER, + args: [], + error: IS_AMERICAN_OR_CANADIAN_ROUTING_NUMBER_ERROR, + }); +}); + test('numberGreaterThan validator produces correct validator object', () => { expect(numberGreaterThan.error).toBe(NUMBER_GREATER_THAN_ERROR); expect(numberGreaterThan('0')).toEqual({ @@ -953,6 +968,71 @@ test('isRoutingNumber validated on empty string', () => { expect(validatorFns[IS_ROUTING_NUMBER]('', [], {})).toBe(true); }); +/** + * isAmericanOrCanadianRoutingNumber Validator + */ +test.prop( + 'isAmericanOrCanadianRoutingNumber validates 8 digit Canadian routing numbers', + [ + fc.integer(0, 9), + fc.integer(0, 9), + fc.integer(0, 9), + fc.integer(0, 9), + fc.integer(0, 9), + fc.integer(0, 9), + fc.integer(0, 9), + fc.integer(0, 9), + ], + (...first8Digits) => { + const validRoutingNumber = first8Digits.join(''); + expect( + validatorFns[IS_AMERICAN_OR_CANADIAN_ROUTING_NUMBER]( + validRoutingNumber, + [], + {} + ) + ).toBe(true); + } +); + +test.prop( + 'isAmericanOrCanadianRoutingNumber validates 9 digit U.S. routing numbers with a valid checksum', + [ + fc.integer(1, 9), + fc.integer(0, 9), + fc.integer(0, 9), + fc.integer(0, 9), + fc.integer(0, 9), + fc.integer(0, 9), + fc.integer(0, 9), + fc.integer(0, 9), + ], + (...first8Digits) => { + const validRoutingNumber = `${first8Digits.join('')}${calcCheckSum( + ...first8Digits + )}`; + expect( + validatorFns[IS_AMERICAN_OR_CANADIAN_ROUTING_NUMBER]( + validRoutingNumber, + [], + {} + ) + ).toBe(true); + } +); + +test('isAmericanOrCanadianRoutingNumber does not validate 9 digit U.S. routing numbers without a valid checksum', () => { + expect(validatorFns[IS_AMERICAN_OR_CANADIAN_ROUTING_NUMBER]('123456789', [], {})).toBe(false); +}); + +test('isAmericanOrCanadianRoutingNumber does not validate strings of lengths less than 8', () => { + expect(validatorFns[IS_AMERICAN_OR_CANADIAN_ROUTING_NUMBER]('1234567', [], {})).toBe(false); +}); + +test('isAmericanOrCanadianRoutingNumber does not validate strings of lengths greater than 9', () => { + expect(validatorFns[IS_AMERICAN_OR_CANADIAN_ROUTING_NUMBER]('1234567891', [], {})).toBe(false); +}); + test('runValidator returns null when validator accepts', () => { expect(runValidator({ type: REQUIRED, args: [] }, 'foo', {})).toBe(null); }); diff --git a/package.json b/package.json index a0b99314..4e1a352a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redux-freeform", - "version": "6.1.0", + "version": "6.1.1", "description": "A small Redux form library that supports purely functional apps, without magic", "main": "dist/redux-freeform.cjs.js", "module": "dist/redux-freeform.esm.js", diff --git a/src/index.js b/src/index.js index 183dc5f2..b2be1266 100644 --- a/src/index.js +++ b/src/index.js @@ -8,6 +8,7 @@ export { hasLength, matchesRegex, isRoutingNumber, + isAmericanOrCanadianRoutingNumber, validateWhen, validateSum, numberGreaterThan, diff --git a/src/validation.js b/src/validation.js index a114f049..b7316a57 100644 --- a/src/validation.js +++ b/src/validation.js @@ -347,6 +347,28 @@ validatorFns[MATCHES_REGEX] = (value, args, form) => { return new RegExp(args[0]).test(value); // new RexExp never throws an error, no matter the input }; +export const IS_AMERICAN_OR_CANADIAN_ROUTING_NUMBER = + 'validator/IS_AMERICAN_OR_CANADIAN_ROUTING_NUMBER'; +export const IS_AMERICAN_OR_CANADIAN_ROUTING_NUMBER_ERROR = + 'validator/IS_AMERICAN_OR_CANADIAN_ROUTING_NUMBER_ERROR'; +export const isAmericanOrCanadianRoutingNumber = createValidator( + IS_AMERICAN_OR_CANADIAN_ROUTING_NUMBER, + IS_AMERICAN_OR_CANADIAN_ROUTING_NUMBER_ERROR +); +validatorFns[IS_AMERICAN_OR_CANADIAN_ROUTING_NUMBER] = (value, args, form) => { + if (value === '') { + return true; + } else if (value.length === 8) { + const regExp = /^\d{8}$/; + return regExp.test(value); + } else if (value.length === 9) { + const regExp = /^\d{9}$/; + return validatorFns[IS_ROUTING_NUMBER](value, args, form) && regExp.test(value); + } else { + return false; + } +}; + // based on http://www.brainjar.com/js/validation/ export const IS_ROUTING_NUMBER = 'validator/IS_ROUTING_NUMBER'; export const IS_ROUTING_NUMBER_ERROR = 'error/IS_ROUTING_NUMBER';