assert = require('assert') Payment = require('../src/index') QJ = require('qj') describe 'payment', -> describe 'Validating a card number', -> it 'should fail if empty', -> topic = Payment.fns.validateCardNumber '' assert.equal topic, false it 'should fail if is a bunch of spaces', -> topic = Payment.fns.validateCardNumber ' ' assert.equal topic, false it 'should success if is valid', -> topic = Payment.fns.validateCardNumber '4242424242424242' assert.equal topic, true it 'that has dashes in it but is valid', -> topic = Payment.fns.validateCardNumber '4242-4242-4242-4242' assert.equal topic, true it 'should succeed if it has spaces in it but is valid', -> topic = Payment.fns.validateCardNumber '4242 4242 4242 4242' assert.equal topic, true it 'that does not pass the luhn checker', -> topic = Payment.fns.validateCardNumber '4242424242424241' assert.equal topic, false it 'should fail if is more than 16 digits', -> topic = Payment.fns.validateCardNumber '42424242424242424' assert.equal topic, false it 'should fail if is less than 10 digits', -> topic = Payment.fns.validateCardNumber '424242424' assert.equal topic, false it 'should fail with non-digits', -> topic = Payment.fns.validateCardNumber '4242424e42424241' assert.equal topic, false describe 'validating card types', -> it 'should validate amex card types', -> assert(Payment.fns.validateCardNumber('378282246310005'), 'amex') assert(Payment.fns.validateCardNumber('371449635398431'), 'amex') assert(Payment.fns.validateCardNumber('378734493671000'), 'amex') it 'should validate dankort card types', -> assert(Payment.fns.validateCardNumber('5019123456789013'), 'dankort') it 'should validate dinersclub card types', -> assert(Payment.fns.validateCardNumber('30569309025904'), 'dinersclub') assert(Payment.fns.validateCardNumber('38520000023237'), 'dinersclub') it 'should validate discover card types', -> assert(Payment.fns.validateCardNumber('6011111111111117'), 'discover') assert(Payment.fns.validateCardNumber('6011000990139424'), 'discover') it 'should validate jcb card types', -> assert(Payment.fns.validateCardNumber('3530111333300000'), 'jcb') assert(Payment.fns.validateCardNumber('3566002020360505'), 'jcb') it 'should validate mastercard card types', -> assert(Payment.fns.validateCardNumber('5555555555554444'), 'mastercard') assert(Payment.fns.validateCardNumber('2221000010000015'), 'mastercard') it 'should validate visa card types', -> assert(Payment.fns.validateCardNumber('4111111111111111'), 'visa') assert(Payment.fns.validateCardNumber('4012888888881881'), 'visa') assert(Payment.fns.validateCardNumber('4222222222222'), 'visa') assert(Payment.fns.validateCardNumber('4000 0000 0000 0000 030'), 'visa') it 'should validate visaelectron card types', -> assert(Payment.fns.validateCardNumber('4917300800000000'), 'visaelectron') it 'should validate unionpay card types', -> assert(Payment.fns.validateCardNumber('6271136264806203568'), 'unionpay') assert(Payment.fns.validateCardNumber('6236265930072952775'), 'unionpay') assert(Payment.fns.validateCardNumber('6204679475679144515'), 'unionpay') assert(Payment.fns.validateCardNumber('6216657720782466507'), 'unionpay') it 'should validate maestro card types', -> assert(Payment.fns.validateCardNumber('6759649826438453'), 'maestro') assert(Payment.fns.validateCardNumber('6759 4111 0000 0008'), 'maestro') assert(Payment.fns.validateCardNumber('6759 6498 2643 8453'), 'maestro') describe 'Validating a CVC', -> it 'should fail if is empty', -> topic = Payment.fns.validateCardCVC '' assert.equal topic, false it 'should pass if is valid', -> topic = Payment.fns.validateCardCVC '123' assert.equal topic, true it 'should fail with non-digits', -> topic = Payment.fns.validateCardNumber '12e' assert.equal topic, false it 'should fail with less than 3 digits', -> topic = Payment.fns.validateCardNumber '12' assert.equal topic, false it 'should fail with more than 4 digits', -> topic = Payment.fns.validateCardNumber '12345' assert.equal topic, false describe 'Validating an expiration date', -> it 'should fail expires is before the current year', -> currentTime = new Date() topic = Payment.fns.validateCardExpiry currentTime.getMonth() + 1, currentTime.getFullYear() - 1 assert.equal topic, false it 'that expires in the current year but before current month', -> currentTime = new Date() topic = Payment.fns.validateCardExpiry currentTime.getMonth(), currentTime.getFullYear() assert.equal topic, false it 'that has an invalid month', -> currentTime = new Date() topic = Payment.fns.validateCardExpiry 0, currentTime.getFullYear() assert.equal topic, false it 'that has an invalid month', -> currentTime = new Date() topic = Payment.fns.validateCardExpiry 13, currentTime.getFullYear() assert.equal topic, false it 'that is this year and month', -> currentTime = new Date() topic = Payment.fns.validateCardExpiry currentTime.getMonth() + 1, currentTime.getFullYear() assert.equal topic, true it 'that is just after this month', -> # Remember - months start with 0 in JavaScript! currentTime = new Date() topic = Payment.fns.validateCardExpiry currentTime.getMonth() + 1, currentTime.getFullYear() assert.equal topic, true it 'that is after this year', -> currentTime = new Date() topic = Payment.fns.validateCardExpiry currentTime.getMonth() + 1, currentTime.getFullYear() + 1 assert.equal topic, true it 'that has string numbers', -> currentTime = new Date() currentTime.setFullYear(currentTime.getFullYear() + 1, currentTime.getMonth() + 2) topic = Payment.fns.validateCardExpiry currentTime.getMonth() + '', currentTime.getFullYear() + '' assert.equal topic, true it 'should validate a string with two digits month and year delimited by slash', -> topic = Payment.fns.validateCardExpiry '03 / 25' assert.equal topic, true it 'should validate a string with two digits month and four digits year delimited by slash', -> topic = Payment.fns.validateCardExpiry '03 / 2025' assert.equal topic, true it 'should fail if expires is string mm/yyyy where the year is before the current year', -> topic = Payment.fns.validateCardExpiry '03 / 205' assert.equal topic, false it 'that has non-numbers', -> topic = Payment.fns.validateCardExpiry 'h12', '3300' assert.equal topic, false it 'should fail if year or month is NaN', -> topic = Payment.fns.validateCardExpiry '12', NaN assert.equal topic, false it 'should support year shorthand', -> assert.equal Payment.fns.validateCardExpiry('05', '20'), true describe 'Validating a CVC number', -> it 'should validate a three digit number with no card type', -> topic = Payment.fns.validateCardCVC('123') assert.equal topic, true it 'should not validate a three digit number with card type amex', -> topic = Payment.fns.validateCardCVC('123', 'amex') assert.equal topic, false it 'should validate a three digit number with card type other than amex', -> topic = Payment.fns.validateCardCVC('123', 'visa') assert.equal topic, true it 'should not validate a four digit number with a card type other than amex', -> topic = Payment.fns.validateCardCVC('1234', 'visa') assert.equal topic, false it 'should validate a four digit number with card type amex', -> topic = Payment.fns.validateCardCVC('1234', 'amex') assert.equal topic, true it 'should not validate a number larger than 4 digits', -> topic = Payment.fns.validateCardCVC('12344') assert.equal topic, false describe 'Parsing an expiry value', -> it 'should parse string expiry', -> topic = Payment.fns.cardExpiryVal('03 / 2025') assert.deepEqual topic, month: 3, year: 2025 it 'should support shorthand year', -> topic = Payment.fns.cardExpiryVal('05/04') assert.deepEqual topic, month: 5, year: 2004 it 'should return NaN when it cannot parse', -> topic = Payment.fns.cardExpiryVal('05/dd') assert isNaN(topic.year) describe 'Getting a card type', -> it 'should return Visa that begins with 40', -> topic = Payment.fns.cardType '4012121212121212' assert.equal topic, 'visa' it 'that begins with 5 should return MasterCard', -> topic = Payment.fns.cardType '5555555555554444' assert.equal topic, 'mastercard' it 'that begins with 2 should return MasterCard', -> topic = Payment.fns.cardType '2221000010000015' assert.equal topic, 'mastercard' it 'that begins with 34 should return American Express', -> topic = Payment.fns.cardType '3412121212121212' assert.equal topic, 'amex' it 'that begins with 457393 should return Elo', -> topic = Payment.fns.cardType '4573931212121212' assert.equal topic, 'elo' it 'should not miscategorize cards with 5067 in them as elo', -> topic = Payment.fns.cardType '4012506712121212' assert.equal topic, 'visa' it 'that is not numbers should return null', -> topic = Payment.fns.cardType 'aoeu' assert.equal topic, null it 'that has unrecognized beginning numbers should return null', -> topic = Payment.fns.cardType 'aoeu' assert.equal topic, null describe 'formatCardNumber', -> it 'should restrict non-number characters', -> number = document.createElement('input') number.type = 'text' QJ.val number, '4242' Payment.formatCardNumber(number) ev = document.createEvent "HTMLEvents" ev.initEvent "keypress", true, true ev.eventName = "keypress" ev.which = "a".charCodeAt(0) number.dispatchEvent(ev) assert.equal QJ.val(number), '4242' it 'should restrict characters after the length', -> number = document.createElement('input') number.type = 'text' QJ.val number, '4242 4242 4242 4242 424' Payment.formatCardNumber(number) ev = document.createEvent "HTMLEvents" ev.initEvent "keypress", true, true ev.eventName = "keypress" ev.which = "4".charCodeAt(0) number.dispatchEvent(ev) assert.equal QJ.val(number), '4242 4242 4242 4242 424' it 'should restrict characters when a generic card number is 16 characters', -> number = document.createElement('input') number.type = 'text' QJ.val(number, '0000 0000 0000 0000') Payment.formatCardNumber(number) ev = document.createEvent "HTMLEvents" ev.initEvent "keypress", true, true ev.eventName = "keypress" ev.which = "0".charCodeAt(0) number.dispatchEvent(ev) assert.equal QJ.val(number), '0000 0000 0000 0000' it 'should restrict characters when a visa card number is greater than 19 characters', -> number = document.createElement('input') number.type = 'text' QJ.val(number, '4000 0000 0000 0000 030') Payment.formatCardNumber(number) ev = document.createEvent "HTMLEvents" ev.initEvent "keypress", true, true ev.eventName = "keypress" ev.which = "0".charCodeAt(0) number.dispatchEvent(ev) assert.equal QJ.val(number), '4000 0000 0000 0000 030' it 'should format cc number correctly', -> number = document.createElement('input') number.type = 'text' QJ.val(number, '4242') Payment.formatCardNumber(number) ev = document.createEvent "HTMLEvents" ev.initEvent "keypress", true, true ev.eventName = "keypress" ev.which = "4".charCodeAt(0) number.dispatchEvent(ev) assert.equal QJ.val(number), '4242 4' it 'should remove a trailing space before a number on backspace ', -> number = document.createElement('input') number.type = 'text' QJ.val(number, '4242 ') number.selectionStart = 5 number.selectionEnd = 5 Payment.formatCardNumber(number) ev = document.createEvent "HTMLEvents" ev.initEvent "keydown", true, true ev.eventName = "keydown" ev.which = 8 number.dispatchEvent(ev) assert.equal QJ.val(number), '424' it 'should remove the space after a number being deleted on a backspace', -> number = document.createElement('input') number.type = 'text' QJ.val(number, '4242 5') number.selectionStart = 6 number.selectionEnd = 6 Payment.formatCardNumber(number) ev = document.createEvent "HTMLEvents" ev.initEvent "keydown", true, true ev.eventName = "keydown" ev.which = 8 number.dispatchEvent(ev) assert.equal QJ.val(number), '4242' it 'should set the card type correctly', -> number = document.createElement('input') number.type = 'text' QJ.val(number, '4') Payment.formatCardNumber(number) ev = document.createEvent "HTMLEvents" ev.initEvent "keyup", true, true ev.eventName = "keyup" number.dispatchEvent(ev) assert QJ.hasClass(number, 'visa') assert QJ.hasClass(number, 'identified') QJ.val(number, '') ev = document.createEvent "HTMLEvents" ev.initEvent "keyup", true, true ev.eventName = "keyup" number.dispatchEvent(ev) # JSDom doesn't support custom events right now, so this testing # doesn't work :( # # eventCalled = false # number.addEventListener 'payment.cardType', -> # console.log 'hiya' # eventCalled = true # assert eventCalled assert QJ.hasClass(number, 'unknown') assert !QJ.hasClass(number, 'identified') it 'should format correctly on paste', -> number = document.createElement('input') number.type = 'text' Payment.formatCardNumber(number) QJ.val(number, '42424') ev = document.createEvent "HTMLEvents" ev.initEvent "paste", true, true ev.eventName = "paste" number.dispatchEvent(ev) # must setTimeout because paste event handling is # done in a setTimeout setTimeout -> assert.equal QJ.val(number), '4242 4' it 'should format cc number correctly when transitioning from one length limit to another', -> number = document.createElement('input') number.type = 'text' QJ.val(number, '4111 1111 1111 1111') Payment.formatCardNumber(number) ev = document.createEvent "HTMLEvents" ev.initEvent "keypress", true, true ev.eventName = "keypress" ev.which = "1".charCodeAt(0) number.dispatchEvent(ev) assert.equal QJ.val(number), '4111 1111 1111 1111 1' describe 'formatCardExpiry', -> it 'should add a slash after two numbers', -> expiry = document.createElement('input') expiry.type = "text" QJ.val(expiry, '1') Payment.formatCardExpiry(expiry) ev = document.createEvent "HTMLEvents" ev.initEvent "keypress", true, true ev.eventName = "keypress" ev.which = "1".charCodeAt(0) expiry.dispatchEvent(ev) assert.equal QJ.val(expiry), '11 / ' it 'should format add a 0 and slash to a number > 1 correctly', -> expiry = document.createElement('input') expiry.type = "text" Payment.formatCardExpiry(expiry) ev = document.createEvent "HTMLEvents" ev.initEvent "keypress", true, true ev.eventName = "keypress" ev.which = "4".charCodeAt(0) expiry.dispatchEvent(ev) assert.equal QJ.val(expiry), '04 / ' it 'should format add a 0 to a number > 1 in the month input correctly', -> month = document.createElement('input') month.type = "text" year = document.createElement('input') year.type = "text" Payment.formatCardExpiry([month, year]) ev = document.createEvent "HTMLEvents" ev.initEvent "keypress", true, true ev.eventName = "keypress" ev.which = "4".charCodeAt(0) month.dispatchEvent(ev) assert.equal QJ.val(month), '04' it 'should format forward slash shorthand correctly', -> expiry = document.createElement('input') expiry.type = "text" QJ.val(expiry, '1') Payment.formatCardExpiry(expiry) ev = document.createEvent "HTMLEvents" ev.initEvent "keypress", true, true ev.eventName = "keypress" ev.which = "/".charCodeAt(0) expiry.dispatchEvent(ev) assert.equal QJ.val(expiry), '01 / ' it 'should only allow numbers', -> expiry = document.createElement('input') expiry.type = "text" QJ.val(expiry, '1') Payment.formatCardExpiry(expiry) ev = document.createEvent "HTMLEvents" ev.initEvent "keypress", true, true ev.eventName = "keypress" ev.which = "d".charCodeAt(0) expiry.dispatchEvent(ev) assert.equal QJ.val(expiry), '1' it 'should remove spaces trailing space and / after removing a number', -> expiry = document.createElement('input') expiry.type = "text" QJ.val(expiry, '12 / 1') Payment.formatCardExpiry(expiry) ev = document.createEvent "HTMLEvents" ev.initEvent "keydown", true, true ev.eventName = "keydown" ev.which = 8 # backspace expiry.dispatchEvent(ev) assert.equal QJ.val(expiry), '12' it 'should a number after removing a space and a /', -> expiry = document.createElement('input') expiry.type = "text" QJ.val(expiry, '12 / ') Payment.formatCardExpiry(expiry) ev = document.createEvent "HTMLEvents" ev.initEvent "keydown", true, true ev.eventName = "keydown" ev.which = 8 # backspace expiry.dispatchEvent(ev) assert.equal QJ.val(expiry), '1' it 'should restrict combined expiry fields to length <= 6 digits', -> expiry = document.createElement('input') expiry.type = "text" QJ.val(expiry, '12 / 2018') Payment.formatCardExpiry(expiry) ev = document.createEvent "HTMLEvents" ev.initEvent "keypress", true, true ev.eventName = "keypress" ev.which = "1".charCodeAt(0) expiry.dispatchEvent(ev) assert.equal QJ.val(expiry), '12 / 2018' it 'should restrict month expiry fields to length <= 2 digits', -> month = document.createElement('input') month.type = "text" QJ.val(month, '12') year = document.createElement('input') year.type = "text" QJ.val(year, '2018') Payment.formatCardExpiryMultiple(month, year) ev = document.createEvent "HTMLEvents" ev.initEvent "keypress", true, true ev.eventName = "keypress" ev.which = "1".charCodeAt(0) month.dispatchEvent(ev) assert.equal QJ.val(month), '12' it 'should restrict year expiry fields to length <= 4 digits', -> month = document.createElement('input') month.type = "text" QJ.val(month, '12') year = document.createElement('input') year.type = "text" QJ.val(year, '2018') Payment.formatCardExpiryMultiple(month, year) ev = document.createEvent "HTMLEvents" ev.initEvent "keypress", true, true ev.eventName = "keypress" ev.which = "1".charCodeAt(0) year.dispatchEvent(ev) assert.equal QJ.val(year), '2018' describe 'formatCVC', -> it 'should allow only numbers', -> cvc = document.createElement('input') cvc.type = "text" QJ.val(cvc, '1') Payment.formatCardCVC(cvc) ev = document.createEvent "HTMLEvents" ev.initEvent "keypress", true, true ev.eventName = "keypress" ev.which = "d".charCodeAt(0) cvc.dispatchEvent(ev) assert.equal QJ.val(cvc), '1' it 'should restrict to length <= 4', -> cvc = document.createElement('input') cvc.type = "text" QJ.val(cvc, '1234') Payment.formatCardCVC(cvc) ev = document.createEvent "HTMLEvents" ev.initEvent "keypress", true, true ev.eventName = "keypress" ev.which = "1".charCodeAt(0) cvc.dispatchEvent(ev) assert.equal QJ.val(cvc), '1234'