Skip to content

Commit

Permalink
feat(uiCreditCard): add credit card mask
Browse files Browse the repository at this point in the history
* Add credit card mask

* adding in global mask

* typo in url

* fix in tests
  • Loading branch information
leonnleite authored and assisrafael committed Jun 26, 2016
1 parent ec007c4 commit 03da0a4
Show file tree
Hide file tree
Showing 5 changed files with 233 additions and 1 deletion.
29 changes: 29 additions & 0 deletions src/global/credit-card/credit-card.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Credit Card Spec</title>
<script src="/node_modules/angular/angular.min.js"></script>
<script src="/releases/angular-input-masks-dependencies.js"></script>
<script src="/releases/angular-input-masks.js"></script>
<script>
angular.module('app', ['ui.utils.masks'])
.controller('ctrl', function ctrl($scope) {
$scope.initializedCC = '4242424242424242';
});
</script>
</head>
<body ng-app="app">
<form name="form" ng-controller="ctrl">
<h2>ui-br-phone-number</h2>
<input type="text" name="freeLinePhoneNumber" ng-model="freeLinePhoneNumber" ng-model-options="{allowInvalid:true}" ui-credit-card><br>
<span ng-bind="freeLinePhoneNumber">-</span> - {{form.freeLinePhoneNumber.$error}}<br>
<br>
<input type="text" name="creditCard" ng-model="creditCard" ng-model-options="{allowInvalid:true}" ui-credit-card><br>
<span ng-bind="creditCard">-</span> - {{form.creditCard.$error}}<br>
<br>
<input type="text" name="initializedCC" ng-model="initializedCC" ng-model-options="{allowInvalid:true}" ui-credit-card><br>
<span ng-bind="initializedCC">-</span> - {{form.initializedCC.$error}}<br>
</form>
</body>
</html>
27 changes: 27 additions & 0 deletions src/global/credit-card/credit-card.js
Original file line number Diff line number Diff line change
@@ -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;
}
}
});
96 changes: 96 additions & 0 deletions src/global/credit-card/credit-card.spec.js
Original file line number Diff line number Diff line change
@@ -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);
}
});
});
});
79 changes: 79 additions & 0 deletions src/global/credit-card/credit-card.test.js
Original file line number Diff line number Diff line change
@@ -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('<input ui-credit-card>');
}).toThrow();
});

it('should register a $parser and a $formatter', function() {
var input = TestUtil.compile('<input ng-model="model">');
var model = input.controller('ngModel');

var maskedInput = TestUtil.compile('<input ng-model="maskedModel" ui-credit-card>');
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('<input ng-model="model" ui-credit-card>', {
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('<input ng-model="model" ng-model-options="{allowInvalid:true}"' +
' ui-credit-card>');
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('<input ng-model="model" ui-credit-card>', {
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('<input ng-model="model" ui-credit-card>', {
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');
});
});
3 changes: 2 additions & 1 deletion src/global/global-masks.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

0 comments on commit 03da0a4

Please sign in to comment.