Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Tests/app/src/Form/TestForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
new Constraints\Url(message: 'Please fill valid url'),
],
])
->add('luhn', TextType::class, [
'constraints' => [
new Constraints\Luhn(message: 'Please fill valid card number'),
],
])

->add('save', SubmitType::class)
;
Expand Down
12 changes: 12 additions & 0 deletions cypress/integration/form_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,5 +209,17 @@ context('JsFormValidatorBundle', () => {
getErrors(fieldId).should('have.length', 0);
});

it('test luhn', () => {
const fieldId = 'test_form_luhn';
cy.get('#' + fieldId).type('1234567890').should('have.value', '1234567890');
submitForm();
getErrors(fieldId).should('have.length', 1);
cy.get('.form-error-test-form-luhn').contains('Please fill valid card number');

cy.get('#' + fieldId).clear().type('79927398713').should('have.value', '79927398713');
submitForm();
getErrors(fieldId).should('have.length', 0);
});

});
});
54 changes: 54 additions & 0 deletions src/Resources/public/js/constraints/Luhn.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//noinspection JSUnusedGlobalSymbols
/**
* Validates that a value passes the Luhn algorithm (typically a credit card number)
* @constructor
* @author dalexandre@jolicode.com
*/
export default function SymfonyComponentValidatorConstraintsLuhn() {
this.message = '';

this.validate = function (value) {
var errors = [];
var f = FpJsFormValidator;

if (f.isValueEmty(value)) {
return errors;
}

// Work with strings only
var strValue = String(value);

// Check if the value contains only digits
if (!/^\d+$/.test(strValue)) {
errors.push(this.message.replace('{{ value }}', FpJsBaseConstraint.formatValue(value)));
return errors;
}

// Luhn algorithm
var checkSum = 0;
var length = strValue.length;

for (var i = length - 1; i >= 0; i--) {
var digit = parseInt(strValue.charAt(i), 10);

if ((i % 2) ^ (length % 2)) {
// Add every second digit starting from the last
checkSum += digit;
} else {
// Double every second digit and add it to the check sum
// For doubles greater than 9, sum the individual digits
var doubled = digit * 2;
checkSum += (doubled >= 10) ? (Math.floor(doubled / 10) + (doubled % 10)) : doubled;
}
}

// Checksum must be non-zero and a multiple of 10
if (0 === checkSum || 0 !== checkSum % 10) {
errors.push(this.message.replace('{{ value }}', FpJsBaseConstraint.formatValue(value)));
}

return errors;
}
}

window.SymfonyComponentValidatorConstraintsLuhn = SymfonyComponentValidatorConstraintsLuhn;
36 changes: 36 additions & 0 deletions src/Resources/public/js/constraints/Luhn.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import '../FpJsFormValidator';
import SymfonyComponentValidatorConstraintsLuhn from './Luhn';

const constraintsLuhn = new SymfonyComponentValidatorConstraintsLuhn();
constraintsLuhn.message = '{{ value }} is not a valid card number';

test.each([
// Valid Luhn numbers
['79927398713', []], // Valid Luhn checksum
['4532015112830366', []], // Example credit card number
['6011111111111117', []], // Example credit card number
['378282246310005', []], // Example AMEX number
['5105105105105100', []], // Example MasterCard number
['4111111111111111', []], // Example Visa number

// Invalid Luhn numbers
['79927398712', ['"79927398712" is not a valid card number']],
['1234567890123456', ['"1234567890123456" is not a valid card number']],
['1111111111111111', ['"1111111111111111" is not a valid card number']],
['0', ['"0" is not a valid card number']], // Single zero fails Luhn
['00', ['"00" is not a valid card number']], // Multiple zeros fail Luhn

// Non-numeric strings
['abc', ['"abc" is not a valid card number']],
['4532-0151-1283-0366', ['"4532-0151-1283-0366" is not a valid card number']], // Contains dashes
['4532 0151 1283 0366', ['"4532 0151 1283 0366" is not a valid card number']], // Contains spaces
['', []], // Empty string
[null, []], // null
[undefined, []], // undefined
[false, []], // false
])(
'SymfonyComponentValidatorConstraintsLuhn',
(value, expected) => {
expect(constraintsLuhn.validate(value)).toStrictEqual(expected);
},
);
1 change: 1 addition & 0 deletions src/Resources/public/js/constraints/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import './IsTrue.js';
import './Length.js';
import './LessThan.js';
import './LessThanOrEqual.js';
import './Luhn.js';
import './NotBlank.js';
import './NotEqualTo.js';
import './NotIdenticalTo.js';
Expand Down
Loading