From 03da0a4223d314f807df4555e7e286929c1e5e81 Mon Sep 17 00:00:00 2001 From: Leonn Leite Date: Sun, 26 Jun 2016 16:07:35 -0300 Subject: [PATCH] feat(uiCreditCard): add credit card mask * Add credit card mask * adding in global mask * typo in url * fix in tests --- src/global/credit-card/credit-card.html | 29 +++++++ src/global/credit-card/credit-card.js | 27 ++++++ src/global/credit-card/credit-card.spec.js | 96 ++++++++++++++++++++++ src/global/credit-card/credit-card.test.js | 79 ++++++++++++++++++ src/global/global-masks.js | 3 +- 5 files changed, 233 insertions(+), 1 deletion(-) create mode 100644 src/global/credit-card/credit-card.html create mode 100644 src/global/credit-card/credit-card.js create mode 100644 src/global/credit-card/credit-card.spec.js create mode 100644 src/global/credit-card/credit-card.test.js diff --git a/src/global/credit-card/credit-card.html b/src/global/credit-card/credit-card.html new file mode 100644 index 00000000..fa4d581c --- /dev/null +++ b/src/global/credit-card/credit-card.html @@ -0,0 +1,29 @@ + + + + + Credit Card Spec + + + + + + +
+

ui-br-phone-number

+
+ - - {{form.freeLinePhoneNumber.$error}}
+
+
+ - - {{form.creditCard.$error}}
+
+
+ - - {{form.initializedCC.$error}}
+
+ + diff --git a/src/global/credit-card/credit-card.js b/src/global/credit-card/credit-card.js new file mode 100644 index 00000000..16321689 --- /dev/null +++ b/src/global/credit-card/credit-card.js @@ -0,0 +1,27 @@ +'use strict'; + +var StringMask = require('string-mask'); +var maskFactory = require('mask-factory'); + +var ccSize = 16; + +var ccMask = new StringMask('0000 0000 0000 0000'); + +module.exports = maskFactory({ + clearValue: function(rawValue) { + return rawValue.toString().replace(/[^0-9]/g, '').slice(0, ccSize); + }, + format: function(cleanValue) { + var formatedValue; + + formatedValue = ccMask.apply(cleanValue) || ''; + + return formatedValue.trim().replace(/[^0-9]$/, ''); + }, + validations: { + creditCard: function(value) { + var valueLength = value && value.toString().length; + return valueLength === ccSize; + } + } +}); diff --git a/src/global/credit-card/credit-card.spec.js b/src/global/credit-card/credit-card.spec.js new file mode 100644 index 00000000..411cccde --- /dev/null +++ b/src/global/credit-card/credit-card.spec.js @@ -0,0 +1,96 @@ +'use strict'; + +describe('ui.utils.masks.number', function() { + it('should load the demo page', function() { + browser.get('/src/global/credit-card/credit-card.html'); + expect(browser.getTitle()).toEqual('Credit Card Spec'); + }); + + describe('ui-credit-card:', function() { + it('should apply a credit card number mask while the user is typping:', function() { + var BS = protractor.Key.BACK_SPACE; + + var tests = [ + {key:'1', viewValue:'1', modelValue:'1'}, + {key:'2', viewValue:'12', modelValue:'12'}, + {key:'3', viewValue:'123', modelValue:'123'}, + {key:'4', viewValue:'1234', modelValue:'1234'}, + {key:'5', viewValue:'1234 5', modelValue:'12345'}, + {key:'6', viewValue:'1234 56', modelValue:'123456'}, + {key:'7', viewValue:'1234 567', modelValue:'1234567'}, + {key:'8', viewValue:'1234 5678', modelValue:'12345678'}, + {key:'9', viewValue:'1234 5678 9', modelValue:'123456789'}, + {key:'0', viewValue:'1234 5678 90', modelValue:'1234567890'}, + {key:'1', viewValue:'1234 5678 901', modelValue:'12345678901'}, + {key:'2', viewValue:'1234 5678 9012', modelValue:'123456789012'}, + {key:'3', viewValue:'1234 5678 9012 3', modelValue:'1234567890123'}, + {key:'4', viewValue:'1234 5678 9012 34', modelValue:'12345678901234'}, + {key:'5', viewValue:'1234 5678 9012 345', modelValue:'123456789012345'}, + {key:'6', viewValue:'1234 5678 9012 3456', modelValue:'1234567890123456'}, + {key:'7', viewValue:'1234 5678 9012 3456', modelValue:'1234567890123456'}, + {key:BS, viewValue:'1234 5678 9012 345', modelValue:'123456789012345'}, + {key:BS, viewValue:'1234 5678 9012 34', modelValue:'12345678901234'}, + {key:BS, viewValue:'1234 5678 9012 3', modelValue:'1234567890123'}, + {key:BS, viewValue:'1234 5678 9012 ', modelValue:'123456789012'}, + {key:BS, viewValue:'1234 5678 9012', modelValue:'123456789012'}, + {key:BS, viewValue:'1234 5678 901', modelValue:'12345678901'}, + {key:BS, viewValue:'1234 5678 90', modelValue:'1234567890'}, + {key:BS, viewValue:'1234 5678 9', modelValue:'123456789'}, + {key:BS, viewValue:'1234 5678 ', modelValue:'12345678'}, + {key:BS, viewValue:'1234 5678', modelValue:'12345678'}, + {key:BS, viewValue:'1234 567', modelValue:'1234567'}, + {key:BS, viewValue:'1234 56', modelValue:'123456'}, + {key:BS, viewValue:'1234 5', modelValue:'12345'}, + {key:BS, viewValue:'1234 ', modelValue:'1234'}, + {key:BS, viewValue:'1234', modelValue:'1234'}, + {key:BS, viewValue:'123', modelValue:'123'}, + {key:BS, viewValue:'12', modelValue:'12'}, + {key:BS, viewValue:'1', modelValue:'1'}, + {key:BS, viewValue:'', modelValue:''}, + ]; + + var input = element(by.model('creditCard')), + value = element(by.exactBinding('creditCard')); + + for (var i = 0; i < tests.length; i++) { + input.sendKeys(tests[i].key); + expect(input.getAttribute('value')).toEqual(tests[i].viewValue); + expect(value.getText()).toEqual(tests[i].modelValue); + } + }); + + it('should apply a credit card number mask in a model with default value:', function() { + var BS = protractor.Key.BACK_SPACE; + + var tests = [ + {key:'1', viewValue:'1', modelValue:'1'}, + {key:'2', viewValue:'12', modelValue:'12'}, + {key:'3', viewValue:'123', modelValue:'123'}, + {key:'4', viewValue:'1234', modelValue:'1234'}, + {key:'5', viewValue:'1234 5', modelValue:'12345'}, + {key:'6', viewValue:'1234 56', modelValue:'123456'}, + {key:'7', viewValue:'1234 567', modelValue:'1234567'}, + {key:BS, viewValue:'1234 56', modelValue:'123456'}, + {key:BS, viewValue:'1234 5', modelValue:'12345'}, + {key:BS, viewValue:'1234 ', modelValue:'1234'}, + {key:BS, viewValue:'1234', modelValue:'1234'}, + {key:BS, viewValue:'123', modelValue:'123'}, + {key:BS, viewValue:'12', modelValue:'12'}, + {key:BS, viewValue:'1', modelValue:'1'}, + {key:BS, viewValue:'', modelValue:''}, + ]; + + var input = element(by.model('initializedCC')), + value = element(by.exactBinding('initializedCC')); + + expect(input.getAttribute('value')).toEqual('4242 4242 4242 4242'); + input.clear(); + + for (var i = 0; i < tests.length; i++) { + input.sendKeys(tests[i].key); + expect(input.getAttribute('value')).toEqual(tests[i].viewValue); + expect(value.getText()).toEqual(tests[i].modelValue); + } + }); + }); +}); diff --git a/src/global/credit-card/credit-card.test.js b/src/global/credit-card/credit-card.test.js new file mode 100644 index 00000000..a31c3892 --- /dev/null +++ b/src/global/credit-card/credit-card.test.js @@ -0,0 +1,79 @@ +'use strict'; + +require('../global-masks'); + +describe('ui-credit-card', function() { + beforeEach(angular.mock.module('ui.utils.masks.global')); + + it('should throw an error if used without ng-model', function() { + expect(function() { + TestUtil.compile(''); + }).toThrow(); + }); + + it('should register a $parser and a $formatter', function() { + var input = TestUtil.compile(''); + var model = input.controller('ngModel'); + + var maskedInput = TestUtil.compile(''); + var maskedModel = maskedInput.controller('ngModel'); + + expect(maskedModel.$parsers.length).toBe(model.$parsers.length + 1); + expect(maskedModel.$formatters.length).toBe(model.$formatters.length + 1); + }); + + it('should format initial model values', function() { + var input = TestUtil.compile('', { + model: '4242424242424242' + }); + + var model = input.controller('ngModel'); + expect(model.$viewValue).toBe('4242 4242 4242 4242'); + }); + + it('should ignore non digits', function() { + var input = TestUtil.compile(''); + var model = input.controller('ngModel'); + + var tests = [ + {value:'@', viewValue:'', modelValue:''}, + {value:'2-', viewValue:'2', modelValue:'2'}, + {value:'23a', viewValue:'23', modelValue:'23'}, + {value:'23_34', viewValue:'2334', modelValue:'2334'}, + {value:'23346!', viewValue:'2334 6', modelValue:'23346'}, + {value:'23346!324', viewValue:'2334 6324', modelValue:'23346324'}, + {value:'23346!324a32', viewValue:'2334 6324 32', modelValue:'2334632432'}, + ]; + + tests.forEach(function(test) { + input.val(test.value).triggerHandler('input'); + expect(model.$viewValue).toBe(test.viewValue); + expect(model.$modelValue).toBe(test.modelValue); + }); + }); + + it('should validate a credit card number', function() { + var input = TestUtil.compile('', { + model: '417900' + }); + + var model = input.controller('ngModel'); + expect(model.$error.creditCard).toBe(true); + input.val('1111222244445555').triggerHandler('input'); + expect(model.$error.creditCard).toBeUndefined(); + }); + + it('should use the type of the model value (if initialized)', function() { + var input = TestUtil.compile('', { + model: '7777333300008888' + }); + + var model = input.controller('ngModel'); + expect(model.$viewValue).toBe('7777 3333 0000 8888'); + expect(model.$modelValue).toBe('7777333300008888'); + input.val('1234098712340987').triggerHandler('input'); + expect(model.$viewValue).toBe('1234 0987 1234 0987'); + expect(model.$modelValue).toBe('1234098712340987'); + }); +}); diff --git a/src/global/global-masks.js b/src/global/global-masks.js index cbd99261..c3fdb746 100644 --- a/src/global/global-masks.js +++ b/src/global/global-masks.js @@ -8,6 +8,7 @@ var m = angular.module('ui.utils.masks.global', [ .directive('uiNumberMask', require('./number/number')) .directive('uiPercentageMask', require('./percentage/percentage')) .directive('uiScientificNotationMask', require('./scientific-notation/scientific-notation')) -.directive('uiTimeMask', require('./time/time')); +.directive('uiTimeMask', require('./time/time')) +.directive('uiCreditCard', require('./credit-card/credit-card')); module.exports = m.name;