Skip to content

Draft: Feature/more rules #54

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from
Open
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
26 changes: 26 additions & 0 deletions __tests__/suits/Area.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -452,4 +452,30 @@ describe('test ValidatorProvider', () => {
area.setProps({ errors: ['test error'] });
expect(area.state().errors.length).toBe(1);
});

it('should get other area', async () => {
Validator.extend('test_other_area', (validator: Validator) => ({
passed(): boolean {
return !!validator.area('other');
},
message(): string {
return 'test';
}
}))

const provider = mount<ValidatorProvider, ValidatorProviderProps>(
<ValidatorProvider rules="test_other_area">
<ValidatorArea>
<input name="test" />
</ValidatorArea>
<ValidatorArea>
<input name="other" />
</ValidatorArea>
</ValidatorProvider>
);

await provider.instance().validate();
await tick();
expect(provider.state().valid).toBeTruthy();
})
})
73 changes: 73 additions & 0 deletions __tests__/suits/rules/length.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React from 'react';
import { mount } from 'enzyme';
import {
Validator,
ValidatorArea,
IncorrectArgumentTypeError,
ValidatorAreaProps
} from '../../../src';
import tick from '../../common/tick';
import length from '../../../src/rules/length';

describe('test length rule', () => {
beforeEach(() => {
Validator.extend('length', length);
});

it('should always validate inputs and not validate non-inputs', async () => {
const input1 = document.createElement('input');
const input2 = document.createElement('input');
const canvas = document.createElement('canvas');
input1.value = 'foo';
input2.value = 'foobar';

const validator_input_incorrect = new Validator([
input1
],
['length:2'],
'');

const validator_input_correct = new Validator([
input2
],
['length:6'],
'');

const validator_canvas = new Validator([
canvas
],
['length:3'],
'');

const validator_wrong_arg = new Validator([
input1
],
['length:foo'],
'');

await validator_input_incorrect.validate();
expect(validator_input_incorrect.getErrors().length).toBe(1);

await validator_input_correct.validate();
expect(validator_input_correct.getErrors().length).toBe(0);

await validator_canvas.validate();
expect(validator_canvas.getErrors().length).toBe(0);

await expect( validator_wrong_arg.validate()).rejects.toBeInstanceOf(IncorrectArgumentTypeError);
});

it('should validate select', async () => {
const area = mount<ValidatorArea, ValidatorAreaProps>(
<ValidatorArea rules="length:5">
<select name="test">
<option value="Test" selected>Option</option>
</select>
</ValidatorArea>
);

area.find('select').simulate('blur');
await tick();
expect(area.state().errors.length).toBe(1);
});
});
73 changes: 73 additions & 0 deletions __tests__/suits/rules/maxLength.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React from 'react';
import { mount } from 'enzyme';
import {
Validator,
ValidatorArea,
IncorrectArgumentTypeError,
ValidatorAreaProps,
maxLength
} from '../../../src';
import tick from '../../common/tick';

describe('test max length rule', () => {
beforeEach(() => {
Validator.extend('maxLength', maxLength);
});

it('should always validate inputs and not validate non-inputs', async () => {
const input1 = document.createElement('input');
const input2 = document.createElement('input');
const canvas = document.createElement('canvas');
input1.value = 'foo';
input2.value = 'foobar';

const validator_input_correct = new Validator([
input1
],
['maxLength:6'],
'');

const validator_input_incorrect = new Validator([
input2
],
['maxLength:4'],
'');

const validator_canvas = new Validator([
canvas
],
['maxLength:3'],
'');

const validator_wrong_arg = new Validator([
input1
],
['maxLength:foo'],
'');

await validator_input_incorrect.validate();
expect(validator_input_incorrect.getErrors().length).toBe(1);

await validator_input_correct.validate();
expect(validator_input_correct.getErrors().length).toBe(0);

await validator_canvas.validate();
expect(validator_canvas.getErrors().length).toBe(0);

await expect( validator_wrong_arg.validate()).rejects.toBeInstanceOf(IncorrectArgumentTypeError);
});

it('should validate select', async () => {
const area = mount<ValidatorArea, ValidatorAreaProps>(
<ValidatorArea rules="maxLength:5">
<select name="test">
<option value="Testlong" selected>Option</option>
</select>
</ValidatorArea>
);

area.find('select').simulate('blur');
await tick();
expect(area.state().errors.length).toBe(1);
});
});
62 changes: 62 additions & 0 deletions __tests__/suits/rules/minLength.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from 'react';
import { mount } from 'enzyme';
import {
Validator,
ValidatorArea,
IncorrectArgumentTypeError,
ValidatorAreaProps
} from '../../../src';
import tick from '../../common/tick';
import minLength from '../../../src/rules/minLength';

describe('test min length rule', () => {
beforeEach(() => {
Validator.extend('minLength', minLength);
});

it('should always validate inputs and not validate non-inputs', async () => {
const input = document.createElement('input');
const canvas = document.createElement('canvas');
input.value = 'foo';

const validator_input = new Validator([
input
],
['minLength:4'],
'');

const validator_canvas = new Validator([
canvas
],
['minLength:3'],
'');

const validator_wrong_arg = new Validator([
input
],
['minLength:foo'],
'');

await validator_input.validate();
expect(validator_input.getErrors().length).toBe(1);

await validator_canvas.validate();
expect(validator_canvas.getErrors().length).toBe(0);

await expect( validator_wrong_arg.validate()).rejects.toBeInstanceOf(IncorrectArgumentTypeError);
});

it('should validate select', async () => {
const area = mount<ValidatorArea, ValidatorAreaProps>(
<ValidatorArea rules="minLength:5">
<select name="test">
<option value="Test" selected>Option</option>
</select>
</ValidatorArea>
);

area.find('select').simulate('blur');
await tick();
expect(area.state().errors.length).toBe(1);
});
});
49 changes: 49 additions & 0 deletions __tests__/suits/rules/same.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Validator, ValidatorProviderProps, ValidatorProviderState } from '../../../src';
import React from 'react';
import same from '../../../src/rules/same';
import { mount } from 'enzyme';
import { ValidatorArea, ValidatorProvider } from '../../../src';
import tick from '../../common/tick';

describe('test same rule', () => {
beforeEach(() => {
Validator.extend('same', same);
});

it('should validate true', async () => {
const area = mount<ValidatorProvider, ValidatorProviderProps, ValidatorProviderState>(
<ValidatorProvider>
<ValidatorArea rules="same:bar">
<input value="foo" name="foo" />
</ValidatorArea>

<ValidatorArea validationName="test">
<input value="foo" name="bar" />
</ValidatorArea>
</ValidatorProvider>
);

await area.instance().validate();
await tick();
expect(area.state().valid).toBeTruthy();
});

it('should validate false', async () => {
const area = mount<ValidatorProvider, ValidatorProviderProps, ValidatorProviderState>(
<ValidatorProvider>
<ValidatorArea rules="same:bar">
<input value="foo" name="foo" />
</ValidatorArea>

<ValidatorArea>
<input value="baz" name="bar" />
</ValidatorArea>
</ValidatorProvider>
);

await area.instance().validate();
await tick();
console.log(area.find(ValidatorArea).first().state());
expect(area.state().valid).toBeFalsy();
});
})
15 changes: 15 additions & 0 deletions __tests__/suits/utils/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { arraysEqual } from '../../../src/common/utils';

describe('utils test', () => {
it('should check if arrays are equal', () => {
expect(arraysEqual(['foo'], ['foo'])).toBeTruthy();
});

it('should check if arrays are not equal', () => {
expect(arraysEqual(['foo'], ['bar'])).toBeFalsy();
});

it('should check if arrays are not equal', () => {
expect(arraysEqual(['foo'], ['foo', 'bar'])).toBeFalsy();
});
});
4 changes: 4 additions & 0 deletions src/Rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ export type RuleObject = {
* Message shown when the rule doesn't pass
*/
message(): string;
/**
* Array of strings to replace the rule args
*/
messageArgs?(): string[];
}

export type Rule = RuleObject | RuleFunction;
30 changes: 24 additions & 6 deletions src/Validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export class Validator {
/**
* Validator area used to access other areas and the provider
*/
private area?: ValidatorArea;
private _area?: ValidatorArea;

/**
* Name used to overwrite name attribute, to allow messages to be more specific
Expand Down Expand Up @@ -151,6 +151,17 @@ export class Validator {
return [name, parameters.split(',')];
}

private static parseArgs(rule: RuleObject, args: string[]): string[] {
if (rule.messageArgs) {
return [
...rule.messageArgs(),
...args.slice(rule.messageArgs().length, args.length - 1)
];
}

return args;
}

/**
* Validate a specific rule
*/
Expand All @@ -165,7 +176,10 @@ export class Validator {
const passed = await ruleObj.passed(this.elements, ...ruleParameters);

if(!passed) {
this.errors.push(this.localize(ruleObj.message(), ...ruleParameters));
this.errors.push(this.localize(
ruleObj.message(),
Validator.parseArgs(ruleObj, ruleParameters)
));
return false;
}

Expand All @@ -192,7 +206,7 @@ export class Validator {
/*
* Get the capitalized, localized message
*/
public localize(message: string, ...ruleArgs: string[]): string {
public localize(message: string, ruleArgs: string[]): string {
return capitalize(this.intl.formatMessage({
id: message,
defaultMessage: message
Expand All @@ -213,7 +227,7 @@ export class Validator {
* Sets the current area
*/
public setArea(area: ValidatorArea): Validator {
this.area = area;
this._area = area;

return this;
}
Expand All @@ -222,8 +236,8 @@ export class Validator {
* Gets the area where this validator instance is used
*/
public getArea(): ValidatorArea {
if (this.area) {
return this.area;
if (this._area) {
return this._area;
}

throw new Error('Areas are only available when validating React components.');
Expand All @@ -236,6 +250,10 @@ export class Validator {
return this.getArea().context.getRefs(name, type);
}

public area(name: string): ValidatorArea | undefined {
return this.getArea().context.getArea(name);
}

/**
* Merges rules from different sources into one array
*/
Expand Down
Loading