From d9c815f97527be23a894a3c064c56236719b9506 Mon Sep 17 00:00:00 2001 From: AleksandrZhukov Date: Fri, 31 May 2019 11:52:16 +0200 Subject: [PATCH] Support of different credit cards types - added support of credit card with different length and formatting - refactored test for credit card mask --- __tests__/mask/credit-card.mask.test.js | 89 +++++++++++++------------ lib/masks/credit-card.mask.js | 58 +++++++--------- package.json | 10 +-- yarn.lock | 5 ++ 4 files changed, 81 insertions(+), 81 deletions(-) diff --git a/__tests__/mask/credit-card.mask.test.js b/__tests__/mask/credit-card.mask.test.js index 0760d828..46f9ebfe 100644 --- a/__tests__/mask/credit-card.mask.test.js +++ b/__tests__/mask/credit-card.mask.test.js @@ -1,5 +1,12 @@ import { CreditCardMask } from '../../lib/masks' +const testCards = { + visa: '4263982640269299', + mastercard: '5425233430109903', + amex: '374245455400126', + maestro: '6759649826438453' +} + test('getType results credit-card', () => { var expected = 'credit-card' var received = CreditCardMask.getType() @@ -7,73 +14,53 @@ test('getType results credit-card', () => { expect(received).toBe(expected) }) -test('1234123412341234 results 1234 1234 1234 1234', () => { +test('basic formatting', () => { var mask = new CreditCardMask() - var expected = '1234 1234 1234 1234' - var received = mask.getValue('1234123412341234') + var expected = '4263 9826 4026 9299' + var received = mask.getValue(testCards.visa) expect(received).toBe(expected) }) -test('1234123412341234 obfuscated true results 1234 **** **** 1234', () => { +test('obfuscated formatting', () => { var mask = new CreditCardMask() - var expected = '1234 **** **** 1234' - var received = mask.getValue('1234123412341234', { + var expected = '4263 **** **** 9299' + var received = mask.getValue(testCards.visa, { obfuscated: true }) expect(received).toBe(expected) }) -test('1234123412341234 obfuscated false results 1234 1234 1234 1234', () => { +test('correct raw value of basic formatting', () => { var mask = new CreditCardMask() - var expected = '1234 1234 1234 1234' - var received = mask.getValue('1234123412341234', { - obfuscated: false - }) + var received = mask.getValue(testCards.visa) - expect(received).toBe(expected) -}) - -test('1234123412341234 obfuscated false results 1234 1234 1234 1234 and raw value [1234, 1234, 1234, 1234]', () => { - var mask = new CreditCardMask() - var options = { - obfuscated: false - } - - var expected = '1234 1234 1234 1234' - var received = mask.getValue('1234123412341234', options) - - var expectedRawValue = ['1234', '1234', '1234', '1234'] - var receivedRawValue = mask.getRawValue(received, options) - - expect(received).toBe(expected) + var expectedRawValue = ['4263', '9826', '4026', '9299'] + var receivedRawValue = mask.getRawValue(received) expectedRawValue.forEach((val, index) => { expect(val).toBe(receivedRawValue[index]) }) }) -test('1234123412341234 obfuscated true results 1234 **** **** 1234 and raw value [1234, ****, ****, 1234]', () => { +test('correct raw value of obfuscated formatting', () => { var mask = new CreditCardMask() var options = { obfuscated: true } - var expected = '1234 **** **** 1234' - var received = mask.getValue('1234123412341234', options) + var received = mask.getValue(testCards.visa, options) - var expectedRawValue = ['1234', '****', '****', '1234'] + var expectedRawValue = ['4263', '****', '****', '9299'] var receivedRawValue = mask.getRawValue(received, options) - expect(received).toBe(expected) - expectedRawValue.forEach((val, index) => { expect(val).toBe(receivedRawValue[index]) }) }) -test('getMask returns 9999 9999 9999 9999', () => { +test('returns correct default regular mask', () => { var mask = new CreditCardMask() var expected = '9999 9999 9999 9999' var received = mask.getMask() @@ -81,26 +68,42 @@ test('getMask returns 9999 9999 9999 9999', () => { expect(received).toBe(expected) }) -test('getMask obfuscated returns 9999 **** **** 9999', () => { +test('returns correct default obfuscated mask', () => { var mask = new CreditCardMask() var expected = '9999 **** **** 9999' - var received = mask.getMask('', { obfuscated: true }) + var received = mask.getMask(null, { obfuscated: true }) + + expect(received).toBe(expected) +}) + +test('basic formatting of amex card', () => { + var mask = new CreditCardMask() + var expected = '3742 454554 00126' + var received = mask.getValue(testCards.amex) expect(received).toBe(expected) }) -test('get masked value with amex issuer must return 1234 123456 12345', () => { +test('obfuscated formatting of amex card', () => { var mask = new CreditCardMask() - var expected = '1234 123456 12345' - var received = mask.getValue('123412345612345', { issuer: 'amex' }) + var expected = '3742 ****** 00126' + var received = mask.getValue(testCards.amex, { obfuscated: true }) expect(received).toBe(expected) }) -test('getMask with diners issuer must return 1234 123456 1234', () => { +test('basic formatting of mastercard card', () => { var mask = new CreditCardMask() - var expected = '1234 123456 1234' - var received = mask.getValue('12341234561234', { issuer: 'diners' }) + var expected = '5425 2334 3010 9903' + var received = mask.getValue(testCards.mastercard) expect(received).toBe(expected) -}) \ No newline at end of file +}) + +test('basic formatting of maestro card', () => { + var mask = new CreditCardMask() + var expected = '6759 6498 2643 8453' + var received = mask.getValue(testCards.maestro) + + expect(received).toBe(expected) +}) diff --git a/lib/masks/credit-card.mask.js b/lib/masks/credit-card.mask.js index 8db3fe7d..40901767 100644 --- a/lib/masks/credit-card.mask.js +++ b/lib/masks/credit-card.mask.js @@ -1,28 +1,16 @@ +import cardTypes from 'credit-card-type' import BaseMask from './_base.mask' import CustomMask from './custom.mask' -const CREDIT_CARD_MASKS = { - 'visa-or-mastercard': { - regular: '9999 9999 9999 9999', - obfuscated: '9999 **** **** 9999' - }, - 'amex': { - regular: '9999 999999 99999', - obfuscated: '9999 ****** 99999' - }, - 'diners': { - regular: '9999 999999 9999', - obfuscated: '9999 ****** 9999' - }, -} +const defaultType = { type: 'default', gaps: [ 4, 8, 12 ], lengths: [ 16 ] } -const CREDIT_CARD_SETTINGS = { - obfuscated: false, - issuer: 'visa-or-mastercard' +const MASK_TRANSLATION = { + '*': val => '*' } -const MASK_TRANSLATION = { - '*': val => null +function setCharAt(str,index,chr) { + if(index > str.length-1) return str; + return str.substr(0,index) + chr + str.substr(index+1); } export default class CreditCardMask extends BaseMask { @@ -31,17 +19,16 @@ export default class CreditCardMask extends BaseMask { } getValue(value, settings) { - let selectedMask = this.getMask(value, settings) return CustomMask.shared.getValue(value, { - mask: selectedMask, + mask: this.getMask(value, settings), translation: MASK_TRANSLATION }) } - validate(value, settings) { + validate(value) { if (!!value) { - let selectedMask = this.getMask(value, settings) - return value.length === selectedMask.length + const type = this.getCardType(value) + return type.lengths.includes(value.length) } return true @@ -57,16 +44,21 @@ export default class CreditCardMask extends BaseMask { }) } - getMask(value, settings) { - let mergedSettings = super.mergeSettings(CREDIT_CARD_SETTINGS, settings) - const selectedMask = this._selectMask(mergedSettings.issuer, mergedSettings.obfuscated) - - return selectedMask + getMask(value, settings = {}) { + const type = this.getCardType(value) + const length = Math.max(...type.lengths) + const gaps = type.gaps + const firstGap = gaps[0]; + const lastGap = gaps[gaps.length - 1]; + let mask = '9'.repeat(length + gaps.length) + if (settings.obfuscated) { + mask = mask.substr(0, firstGap) + '*'.repeat(lastGap - firstGap + 2) + mask.substr(lastGap + 2) + } + gaps.forEach((index, i) => mask = setCharAt(mask, index + i, ' ')) + return mask } - _selectMask(issuer, obfuscated) { - const maskType = obfuscated ? 'obfuscated' : 'regular' - - return CREDIT_CARD_MASKS[issuer][maskType] + getCardType(value) { + return cardTypes(value)[0] || defaultType } } diff --git a/package.json b/package.json index 1060d91f..e83d3d58 100644 --- a/package.json +++ b/package.json @@ -36,21 +36,21 @@ }, "homepage": "https://github.com/benhurott/react-native-masked-text#readme", "dependencies": { + "credit-card-type": "^8.2.0", "moment": "2.19.3", "tinymask": "^1.0.2" }, "devDependencies": { - "@types/react-native": "*", "@types/react": "*", + "@types/react-native": "*", "babel-cli": "^6.26.0", - "rimraf": "^2.6.2", - "react": "16.3.1", - "react-native": "0.55.4", "babel-jest": "23.4.2", "babel-preset-react-native": "4.0.0", "jest": "23.5.0", + "react": "16.3.1", + "react-native": "0.55.4", "react-test-renderer": "16.3.1", - "react-native-masked-text": "1.9.0" + "rimraf": "^2.6.2" }, "jest": { "preset": "react-native" diff --git a/yarn.lock b/yarn.lock index bb455c5e..19312f96 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1998,6 +1998,11 @@ create-react-class@^15.6.3: loose-envify "^1.3.1" object-assign "^4.1.1" +credit-card-type@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/credit-card-type/-/credit-card-type-8.2.0.tgz#507a43b257f0f3b8bd4d4b339a419741fc7e6236" + integrity sha512-+1kZKW717eDTbZ1/EjkEi4+UrD2SygU2Iujn/VlrMHXloGW/y5GqPlQ/KgT4gaoDmWyufBfm/CAzNkpeccfMtA== + cross-spawn@^5.0.1, cross-spawn@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"