Skip to content

Commit

Permalink
feat(): add integer rule
Browse files Browse the repository at this point in the history
BREAKING CHANGE: number rule accepts float number now
  • Loading branch information
horprogs committed Dec 28, 2022
1 parent d2b83fe commit 9b805fe
Show file tree
Hide file tree
Showing 10 changed files with 248 additions and 6 deletions.
12 changes: 12 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,18 @@ <h2>
id="example2_input_number"
/>
</div>
<div class="form-group mt-3">
<label for="example2_input_integer_number"
>Input an integer number</label
>
<input
class="form__input form-control"
placeholder="Input a number"
autocomplete="off"
name="example2_input_integer_number"
id="example2_input_integer_number"
/>
</div>
<div class="form-group mt-3">
<label for="example2_input_number_between"
>Input a number between 10 and 20</label
Expand Down
9 changes: 8 additions & 1 deletion site/documentation/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1248,7 +1248,14 @@ <h1 class="docs-heading">
<td>
<code>number</code>
</td>
<td>Value should be a number</td>
<td>Value should be a number (integer or float)</td>
<td>-</td>
</tr>
<tr>
<td>
<code>integer</code>
</td>
<td>Value should be an integer number</td>
<td>-</td>
</tr>
<tr>
Expand Down
20 changes: 20 additions & 0 deletions site/examples/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,18 @@ <h2>
id="example2_input_number"
/>
</div>
<div class="form-group mt-3">
<label for="example2_input_integer_number"
>Input an integer number</label
>
<input
class="form__input form-control"
placeholder="Input a number"
autocomplete="off"
name="example2_input_integer_number"
id="example2_input_integer_number"
/>
</div>
<div class="form-group mt-3">
<label for="example2_input_number_between"
>Input a number between 10 and 20</label
Expand Down Expand Up @@ -525,6 +537,14 @@ <h2>
rule: 'number',
},
])
.addField('#example2_input_integer_number', [
{
rule: 'required',
},
{
rule: 'integer',
},
])
.addField('#example2_input_number_between', [
{
rule: 'required',
Expand Down
8 changes: 8 additions & 0 deletions src/demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,14 @@ const advanced = (): void => {
rule: 'number' as Rules,
},
])
.addField('#example2_input_integer_number', [
{
rule: 'required' as Rules,
},
{
rule: 'integer' as Rules,
},
])
.addField('#example2_input_number_between', [
{
rule: 'required' as Rules,
Expand Down
12 changes: 12 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
isPassword,
isStrongPassword,
isInvalidOrEmptyString,
isInteger,
} from './utils/validationUtils';
import {
EventListenerInterface,
Expand Down Expand Up @@ -454,6 +455,17 @@ class JustValidate {
break;
}

case Rules.Integer: {
if (isInvalidOrEmptyString(elemValue)) {
break;
}

if (!isInteger(elemValue as string)) {
this.setFieldInvalid(key, fieldRule);
}
break;
}

case Rules.MaxNumber: {
if (ruleValue === undefined) {
console.error(
Expand Down
1 change: 1 addition & 0 deletions src/modules/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export enum Rules {
MaxLength = 'maxLength',
Password = 'password',
Number = 'number',
Integer = 'integer',
MaxNumber = 'maxNumber',
MinNumber = 'minNumber',
StrongPassword = 'strongPassword',
Expand Down
32 changes: 32 additions & 0 deletions src/tests/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1166,6 +1166,22 @@ describe('Validation', () => {
expect(onSubmit).toHaveBeenCalled();
onSubmit.mockReset();

validation.addField('#consent_checkbox', [
{
rule: Rules.Integer,
},
]);

await clickBySelector('#submit-btn');
await waitFor(() => {
expect(getElem('#submit-btn')).toBeEnabled();
});
expect(
getElemByKey('error-label', '#consent_checkbox', validation)
).not.toBeInTheDocument();
expect(onSubmit).toHaveBeenCalled();
onSubmit.mockReset();

validation.addField('#consent_checkbox', [
{
rule: Rules.MaxNumber,
Expand Down Expand Up @@ -1309,6 +1325,22 @@ describe('Validation', () => {
expect(onSubmit).toHaveBeenCalled();
onSubmit.mockReset();

validation.addField('#email', [
{
rule: Rules.Integer,
},
]);
await changeTextBySelector('#email', '');
await clickBySelector('#submit-btn');
await waitFor(() => {
expect(getElem('#submit-btn')).toBeEnabled();
});
expect(
getElemByKey('error-label', '#email', validation)
).not.toBeInTheDocument();
expect(onSubmit).toHaveBeenCalled();
onSubmit.mockReset();

validation.addField('#email', [
{
rule: Rules.MaxNumber,
Expand Down
42 changes: 40 additions & 2 deletions src/tests/rules.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
isStrongPassword,
isNumber,
isInvalidOrEmptyString,
isInteger,
} from '../utils/validationUtils';

describe('Validate rules', () => {
Expand Down Expand Up @@ -47,12 +48,49 @@ describe('Validate rules', () => {
expect(isNumber('10')).toBeTruthy();
expect(isNumber('01')).toBeTruthy();
expect(isNumber('00')).toBeTruthy();
expect(isNumber('-10')).toBeTruthy();
expect(isNumber('-10.10')).toBeTruthy();
expect(isNumber('d1')).toBeFalsy();
expect(isNumber('1d')).toBeFalsy();
expect(isNumber('1d1')).toBeFalsy();
expect(isNumber('f11')).toBeFalsy();
expect(isNumber('11.')).toBeFalsy();
expect(isNumber('.11')).toBeFalsy();
expect(isNumber('11.')).toBeTruthy();
expect(isNumber('11.1.1')).toBeFalsy();
expect(isNumber('-11.')).toBeTruthy();
expect(isNumber('.11')).toBeTruthy();
expect(isNumber('1.1f')).toBeFalsy();
expect(isNumber('100,10')).toBeFalsy();
expect(isNumber('-')).toBeFalsy();
// @ts-ignore
expect(isNumber(null)).toBeFalsy();
expect(isNumber('1.0')).toBeTruthy();
expect(isNumber('100.10')).toBeTruthy();
});

test('isInteger', () => {
expect(isInteger('10')).toBeTruthy();
expect(isInteger('0')).toBeTruthy();
expect(isInteger('10')).toBeTruthy();
expect(isInteger('01')).toBeTruthy();
expect(isInteger('00')).toBeTruthy();
expect(isInteger('-10')).toBeTruthy();
expect(isInteger('-10.10')).toBeFalsy();
expect(isInteger('d1')).toBeFalsy();
expect(isInteger('1d')).toBeFalsy();
expect(isInteger('1d1')).toBeFalsy();
expect(isInteger('f11')).toBeFalsy();
expect(isInteger('11.')).toBeFalsy();
expect(isInteger('11.1.1')).toBeFalsy();
expect(isInteger('-11.')).toBeFalsy();
expect(isInteger('.11')).toBeFalsy();
expect(isInteger('1.1f')).toBeFalsy();
expect(isInteger('100,10')).toBeFalsy();
// @ts-ignore
expect(isInteger(null)).toBeFalsy();
expect(isInteger('1.0')).toBeFalsy();
expect(isInteger('100.10')).toBeFalsy();
expect(isInteger('')).toBeFalsy();
expect(isInteger('-')).toBeFalsy();
});

test('isMinLength', () => {
Expand Down
106 changes: 105 additions & 1 deletion src/tests/validateRules.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
import { waitFor } from '@testing-library/dom';

describe('validateRules', () => {
it('should be able to validate numbers', async () => {
it('should be able to validate number range', async () => {
const onSubmit = jest.fn();

const validation = new JustValidate('#form', {
Expand Down Expand Up @@ -113,6 +113,110 @@ describe('validateRules', () => {
).not.toBeInTheDocument();
});

it('should be able to validate numbers', async () => {
const onSubmit = jest.fn();

const validation = new JustValidate('#form', {
testingMode: true,
});

validation
.addField('#name', [
{
rule: Rules.Required,
},
{
rule: Rules.Number,
},
])
.onSuccess(onSubmit);

await changeTextBySelector('#name', '-10.2');
await clickBySelector('#submit-btn');
await waitFor(() => {
expect(getElem('#submit-btn')).toBeEnabled();
});
expect(onSubmit).toHaveBeenCalled();
expect(
getElemByKey('error-label', '#name', validation)
).not.toBeInTheDocument();
onSubmit.mockReset();

await changeTextBySelector('#name', '-10.2f');
await clickBySelector('#submit-btn');
await waitFor(() => {
expect(getElem('#submit-btn')).toBeEnabled();
});
expect(onSubmit).not.toHaveBeenCalled();
expect(
getElemByKey('error-label', '#name', validation)
).toBeInTheDocument();
onSubmit.mockReset();
});

it('should be able to validate numbers', async () => {
const onSubmit = jest.fn();

const validation = new JustValidate('#form', {
testingMode: true,
});

validation
.addField('#name', [
{
rule: Rules.Required,
},
{
rule: Rules.Integer,
},
])
.onSuccess(onSubmit);

await changeTextBySelector('#name', '-10.2');
await clickBySelector('#submit-btn');
await waitFor(() => {
expect(getElem('#submit-btn')).toBeEnabled();
});
expect(onSubmit).not.toHaveBeenCalled();
expect(
getElemByKey('error-label', '#name', validation)
).toBeInTheDocument();
onSubmit.mockReset();

await changeTextBySelector('#name', '10.2');
await clickBySelector('#submit-btn');
await waitFor(() => {
expect(getElem('#submit-btn')).toBeEnabled();
});
expect(onSubmit).not.toHaveBeenCalled();
expect(
getElemByKey('error-label', '#name', validation)
).toBeInTheDocument();
onSubmit.mockReset();

await changeTextBySelector('#name', '-10');
await clickBySelector('#submit-btn');
await waitFor(() => {
expect(getElem('#submit-btn')).toBeEnabled();
});
expect(onSubmit).toHaveBeenCalled();
expect(
getElemByKey('error-label', '#name', validation)
).not.toBeInTheDocument();
onSubmit.mockReset();

await changeTextBySelector('#name', '10');
await clickBySelector('#submit-btn');
await waitFor(() => {
expect(getElem('#submit-btn')).toBeEnabled();
});
expect(onSubmit).toHaveBeenCalled();
expect(
getElemByKey('error-label', '#name', validation)
).not.toBeInTheDocument();
onSubmit.mockReset();
});

it('should be able to validate password', async () => {
const onSubmit = jest.fn();

Expand Down
12 changes: 10 additions & 2 deletions src/utils/validationUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ElemValueType } from '../modules/interfaces';

const EMAIL_REGEXP =
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
const NUMBER_REGEXP = /^[0-9]+$/;
const INTEGER_REGEXP = /^-?[0-9]\d*$/;
// Minimum eight characters, at least one letter and one number
const PASSWORD_REGEXP = /^(?=.*[A-Za-z])(?=.*\d).{8,}$/;
// Minimum eight characters, at least one uppercase letter, one lowercase letter, one number and one special character
Expand Down Expand Up @@ -32,7 +32,15 @@ export const isLengthLessThanMin = (value: string, len: number): boolean => {
};

export const isNumber = (value: string): boolean => {
return NUMBER_REGEXP.test(value);
if (typeof value !== 'string') {
return false;
}

return !isNaN(+value) && !isNaN(parseFloat(value));
};

export const isInteger = (value: string): boolean => {
return INTEGER_REGEXP.test(value);
};

export const isPassword = (value: string): boolean => {
Expand Down

0 comments on commit 9b805fe

Please sign in to comment.