oussamahamdaoui/forgJs

View on GitHub
README.md

Summary

Maintainability
Test Coverage
# ![forgJs logo](./media/logo.png?raw=true)

ForgJs is a JavaScript lightweight object validator. Go check the Quick start section and start coding with love :heart:

![email](./media/email.gif?raw=true)

![password](./media/password.gif?raw=true)

![number](./media/number.gif?raw=true)

![url](./media/url.gif?raw=true)

See more live examples [here](https://oussamahamdaoui.github.io/forgjs-examples/)

# Quick start

Install it via npm by running `npm i @cesium133/forgjs`

## Your first validator

```javascript
  const { Validator, Rule } = require('@cesium133/forgjs');

const emailRule = new Rule({
    type: 'email',
    user: user => user === 'dedede',
    domain: domain => ['outlook', 'gmail', 'yahoo'].indexOf(domain) !== -1,
}, null);

const passwordRule = new Rule({
    type: 'password',
    minLength: 8,
    uppercase: 1,
    numbers: 1,
    matchesOneOf: ['@', '_', '-', '.', '!'],
}, null);

const vComplex = new Validator({
    age: new Rule({ type: 'int', min: 18, max: 99 }),
    dateOfBirth: new Rule({ type: 'date' }),
    array: new Rule({ type: 'array', of: new Rule({ type: 'string' }) }),
    email: emailRule,
    password: passwordRule
});

vComplex.test({
    age: 26,
    dateOfBirth: new Date(1995, 10, 3),
    array: ['1'],
    email: 'dedede@yahoo.fr;',
    password: 'ad1_A@@Axs',
}); /// returns true
  ```

## Error handling

You can get custom error messages by doing this:

```javascript
const vComplexe = new Validator({
    age: new Rule({
      type: 'int', min: 18, max: 99,
    }, 'age must be integer and between 18 and 99'),
    dateOfBirth: new Rule({ type: 'date' }, 'date must be a date'),
  });

  vComplexe.getErrors({
    age: 16,
    dateOfBirth: 123,
  }); // ['age must be integer and between 18 and 99', 'date must be a date']

```
## Test over an array

You can test over multiple arrays using this method:

```javascript
const vComplexe = new Validator({
    age: new Rule({
      type: 'int', min: 18, max: 99,
    }),
  });

  vComplexe.testAll([{
    age: 19,
  }, {
    age: 16,
  }]);  // returns 1
```

# Rules

A `Rule` object validates a single value, it can be used like this: 

```javascript
  const { Validator, Rule } = require('@cesium133/forgjs');
  const floatRule = new Rule({
    type: 'float',
    min: 100,
  }, null);

  floatRule.test(2.001); /// returns true;
```

**The only required value is `type`!**

> You can make a rule by simply passing a string if you only need to check the type : `new Rule('int');`

## int

* min (int)
* max (int)
* equal (int)

## boolean

* toBe (boolean)

## string

* minLength (int)
* maxLength (int)
* equal (string)
* match: (regex)
* notEmpty (bool)

## email

* minLength (int)
* maxLength (int)
* equal (string)
* match: (regex)
* notEmpty (bool)
* user (`function(user)`)
* domain (`function(domain)`)

```javascript
  const emailRule = new Rule({
    type: 'email',
    user: user => user === 'dedede',
    domain: domain => ['outlook', 'gmail', 'yahoo'].indexOf(domain) !== -1,
  }, null);

  emailRule.test('dedede@gmail.fr'); // returns true
```

## password

* minLength (int)
* maxLength (int)
* equal (string)
* match: (regex)
* notEmpty (bool)
* uppercase (int)
* number (int)
* mathesOneOf (Array)
* matchesAllOf (Array)

```javascript
  const passwordRule = new Rule({
    type: 'password',
    minLength: 8,
    uppercase: 1,
    numbers: 1,
    matchesOneOf: ['@', '_', '-', '.', '!'],
  }, null);

  passwordRule.test('@_-bddcd6A'); // returns true
```

## url

* minLength (int)
* maxLength (int)
* equal (string)
* match: (regex)
* notEmpty (bool)
* protocol (`function(protocol)`)
* domain (`function(domain)`)

```javascript
  const urlRule = new Rule({
    type: 'url',
    protocol: prot => prot === 'https',
    domain: domain => domain === 'google.fr',
  }, null);

  urlRule.test('https://google.fr'); // returns true
```

## date

* after (date)
* before (date)
* between (Array of dates like this [date, date])
* equal (date)

## float

* min (Number)
* max (Number)
* equal (float)

## array

* of (Rule or Validator object)
* notEmpty (bool)
* length (int)

The `of` rule checks every element of the array against the rule.

## function

* result

To explain result, what's better than an example:

```javascript
  const { Validator, Rule } = require('@cesium133/forgjs');

  function someFunctionThatReturnsAnInt(int) {
    return int * 5;
  }

  const functionTest = new Rule({
    type: 'function',
    result: {
      of: 5,
      toBe: new Rule('int'),
    },
  }, null);

  functionTest.test(someFunctionThatReturnsAnInt); /// returns true;

  ```
## string-int, string-float, string-date, string-boolean

These types 'inherit' from string, they have both the properties, here are some examples:

### string-int

```javascript
const stringInt = new Rule({
  type: 'string-int',
  minLength: 2,
  min: 5,
}, null);

stringInt.test(2) // returns false 2 is not a string
stringInt.test('2a') // returns false '2a' is not a int
stringInt.test('2.1') // returns false '2.1' is not a int
stringInt.test('5') // returns false length of '5' is smaller than 2
stringInt.test('50') // returns true
```
### string-boolean

```javascript
const stringBoolean = new Rule({
  type: 'string-boolean',
  toBe: true
}, null);

stringBoolean.test(true) // returns false true is not a boolean
stringBoolean.test('false') // returns false 'false' is not true
stringBoolean.test('true') // returns true

```

```javascript
const stringDate = new Rule({
  type: 'string-date',
  after: new Date(2019, 11, 1),
}, null);

stringDate.test(new Date(2018, 11, 1)) // returns false new Date(2018, 11, 1) is not a string
stringDate.test('some string') // returns false 'some string' is not a valid date
stringDate.test('2018-12-17') // returns false '2018-12-17' is not after new Date(2019, 11, 1)
stringDate.test('2020-01-01') // returns true
```

**Forgjs tries to cast the value to the right type before passing it to the validation function please be careful!**

Here is an example where Javascript behaviour makes the test wrong:

```javascript
const stringDate = new Rule({
  type: 'string-date',
  equal: new Date(2019, 10, 1), // month in js strart at 0
}, null);

stringDate.test('2019-11-01') // returns false
stringDate.test('2019-11-01T00:00') // returns true


// this is because:

new Date(2019, 10, 1) - new Date('2019-11-01') // equals  3600000 thats exactly 1 hour

new Date(2019, 10, 1) - new Date('2019-11-01T00:00') // equals 0

```

## Multiple types

You can check for multiple types with `OR` or `AND` operators like this:

```javascript
  const intRule = new Rule({
    type: 'int|float|number',
  }, null);

  intRule.test(2) // returns true
```

This means the test should verify the `int`, `float` or `number` rule

```javascript
  const intRule = new Rule({
    type: 'int&number',
  }, null);
  intRule.test(2.1); // returns false
```

The result doesn't match the `int` rule

## Common properties

Every type has these properties:

* optional
* custom
* oneOf

### optional

If optional is set to `true` the element is optional and an `undefined` value is considered correct.
Example:

```javascript
const { Validator, Rule } = require('@cesium133/forgjs');

const intRule = new Rule({
    type: 'int',
    optional: true,
  }, null);
intRule.test(); // returns true
```

### custom

Custom allows you to write your own rule, an example is better than a long explanation:

```javascript
  const { Validator, Rule } = require('@cesium133/forgjs');
  
  function isCorrectAge(age, object) {
    if (age === Math.floor((new Date() - object.dateOfBirth) / 1000 / 60 / 60 / 24 / 30 / 12)) {
      return true;
    }
    return false;
  }
  const vComplexe = new Validator({
    age: new Rule({
      type: 'int', min: 18, max: 99, custom: isCorrectAge,
    }),
    dateOfBirth: new Rule({ type: 'date' }),
  });

  vComplexe.test({
    age: 23,
    dateOfBirth: new Date(1995, 10, 3),
    array: ['1'],
  }); // returns true

```
### oneOf

One of checks if the element is in a array
```javascript
  const floatRule = new Rule({
    type: 'float',
    oneOf: [3.5, 100.1, 7.2, 0.1],
  }, null);
  floatRule.test(100.1); // returns true
```

# Make a new type

Creating a new type is done using the Rule class like this:

```javascript
  const { Validator, Rule } = require('@cesium133/forgjs'); 
  
  Rule.addCustom('customInteger', {
    min: (val, min) => val - min > 0,
    max: (val, max) => val - max < 0,
    equal: (val, equal) => val === equal,
    type: val => Number.isInteger(val) && val > 0 && val < 100,
  });

  const customInteger = new Rule({
    type: 'customInteger',
    min: 10,
  }, null);

  customInteger.test(11) // returns true

  customInteger.test(200) // returns false

```

# How to contribute

Thank you everyone for contributing to make this code better, if you have suggestions or ideas to improve the code please feel free to leave a comment here #29.
Rules:

### 1 Please use this template which will help developers to test and better understand your request

```javascript
const someRule= new Rule({
    type: 'yourType',
    prop1: val1,
    prop2: val2, ...
  }, null);

  someRule.test(validValue) // returns true
  someRule.test(invalidValue) // returns false
```

## 2 Please if you think a comment is a good feature to be added like the comment instead of creating a new one.

## 3 Before submitting a new comment check if the same comment is not already present

## 4 If you submit a PR (pull request) and you only change the Readme please add `[ci skip]` to your commit message

## 5 If you have any questions ask them in the FAQ

## 6 Please have fun, and if you feel like not following the rules then don't follow them

code with love ❤️

# Left TO DO for next release

# Contact

Follow me on twitter at [@forg_js](https://twitter.com/forg_js "@forg_js")