Skip to content

Feature/setup #1

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

Merged
merged 10 commits into from
Aug 23, 2020
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
27 changes: 27 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint/eslint-plugin"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended"
],
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module",
"project": "./tsconfig.json"
},
"env": {
"node": true,
"es6": true
},
"rules": {
"comma-dangle": "off",
"class-methods-use-this": "off",
"import/prefer-default-export": "off",
"import/no-dynamic-require": "off",
"global-require": "off",
"quotes": ["error", "single", { "allowTemplateLiterals": true }],
"@typescript-eslint/indent": ["error", 4]
}
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

# production
/build
/dist

# misc
.DS_Store
Expand Down
129 changes: 129 additions & 0 deletions __tests__/Area.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import React from 'react';
import { mount } from 'enzyme';
import { ValidatorArea, Validator } from '../src';
import { ValidatorAreaProps } from '../src/ValidatorArea';
import required from '../src/rules/required';

describe('test Provider', () => {
it('should render input', () => {
const area = mount<ValidatorArea, ValidatorAreaProps>(
<ValidatorArea>
<input name="test" />
</ValidatorArea>
);

expect(area.find('input')).toBeDefined();
});

it('should render inputs with callback as child', () => {
const area = mount<ValidatorArea, ValidatorAreaProps>(
<ValidatorArea>
{() => (
<input name="test" />
)}
</ValidatorArea>
);

expect(area.find('input')).toBeDefined();
})

it('should throw an exception when no name provided', () => {
const area = () => {
mount<ValidatorArea, ValidatorAreaProps>(
<ValidatorArea>
{() => (
<>
<input/>
<input/>
</>
)}
</ValidatorArea>
);
}
expect(() => area()).toThrow();
});

it('should index (nested) inputs', () => {
const area = mount<ValidatorArea, ValidatorAreaProps>(
<ValidatorArea name="test">
{() => (
<>
<><input/></>
<div>
<input />
<input />
<><input/></>
</div>
</>
)}
</ValidatorArea>
);

expect(area.instance().getInputRefs().length).toBe(4);
});

it('should apply rules on blur', () => {
const area = mount<ValidatorArea, ValidatorAreaProps>(
<ValidatorArea rules={[required]}>
<input name="test" />
</ValidatorArea>
);

area.find('input').at(0).simulate('blur');
expect(area.state().errors[0]).toBe('This field is required');
});

it('should render error when area dirty', async () => {
const area = mount<ValidatorArea, ValidatorAreaProps>(
<ValidatorArea rules={[required]}>
{({ errors }) => (
<>
<input name="test" />
{errors.length && <div>{errors[0]}</div>}
</>
)}
</ValidatorArea>
);

area.find('input').simulate('blur');
expect(area.find('div').text()).toBe('This field is required');
})

it('should validate element with rule string', () => {
Validator.extend('testrule', {
passed(): boolean {
return false;
},
message(): string {
return 'string rule passed';
}
});

const area = mount<ValidatorArea, ValidatorAreaProps>(
<ValidatorArea rules={['testrule']}>
{({ errors }) => (
<>
<input name="test" />
{errors.length && <div>{errors[0]}</div>}
</>
)}
</ValidatorArea>
);

area.find('input').simulate('blur');
expect(area.find('div').text()).toBe('string rule passed');
})

it('should call element\'s provided blur along validator blur', () => {
const mockFn = jest.fn();

const area = mount<ValidatorArea, ValidatorAreaProps>(
<ValidatorArea rules={[required]}>
<input name="test" onBlur={mockFn} />
</ValidatorArea>
);

area.find('input').simulate('blur');
expect(mockFn).toBeCalled();
});
})
131 changes: 131 additions & 0 deletions __tests__/Provider.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import React from 'react';
import { mount } from 'enzyme';
import { ValidatorArea, ValidatorProvider } from '../src';
import required from '../src/rules/required';
import { ValidatorProviderProps } from '../src/Provider';


const tick = () => {
return new Promise(resolve => {
setTimeout(resolve, 0);
})
}

describe('test Provider', () => {
it('should render Provider', () => {
const provider = mount<ValidatorProvider, ValidatorProviderProps>(
<ValidatorProvider rules={[required]} />
);

expect(provider.instance().props.rules).toBeDefined();
});

it('should throw error when area with existing name is addeded', () => {
const provider = () => {
mount<ValidatorProvider, ValidatorProviderProps>(
<ValidatorProvider>
<ValidatorArea name="test">
<div />
</ValidatorArea>
<ValidatorArea name="test">
<div />
</ValidatorArea>
</ValidatorProvider>
);
}

expect(() => provider()).toThrow('Validation area names should be unique');
})

it('should render with function as child', () => {
const provider = mount<ValidatorProvider, ValidatorProviderProps>(
<ValidatorProvider>
{() => <div>test</div>}
</ValidatorProvider>
);

expect(provider.find('div').text()).toBe('test');
})

it('should add an area when provided as child', () => {
const provider = mount<ValidatorProvider, ValidatorProviderProps>(
<ValidatorProvider>
<ValidatorArea name="test">
<div />
</ValidatorArea>
</ValidatorProvider>
);

const areas = provider.state().areas;

expect(areas.test).toBeDefined();
expect(areas.test).toBeInstanceOf(ValidatorArea);
})

it('should not call callback when areas dirty', async () => {
const mockFn = jest.fn();

const provider = mount<ValidatorProvider, ValidatorProviderProps>(
<ValidatorProvider>
{({ validate }) => (
<>
<ValidatorArea rules={[required]} name="test1">
<input value="" />
</ValidatorArea>
<ValidatorArea rules={[required]} name="test2">
<input value="" />
</ValidatorArea>
<button onClick={() => validate(mockFn)} />
</>
)}
</ValidatorProvider>
);

provider.find('button').simulate('click');
await tick();
expect(mockFn).not.toHaveBeenCalled()
})

it('should call callback when areas not dirty', async () => {
const mockFn = jest.fn();

const provider = mount<ValidatorProvider, ValidatorProviderProps>(
<ValidatorProvider>
{({ validate }) => (
<>
<ValidatorArea rules={[required]} name="test1">
<input value="foo" />
</ValidatorArea>
<button onClick={() => validate(mockFn)} />
</>
)}
</ValidatorProvider>
);

provider.find('button').simulate('click');
await tick();
expect(mockFn).toHaveBeenCalled()
})


it('should just validate when not rules prop is given', async () => {
const mockFn = jest.fn();

const provider = mount<ValidatorProvider, ValidatorProviderProps>(
<ValidatorProvider>
{({ validate }) => (
<>
<ValidatorArea name="test1">
<input value="" />
</ValidatorArea>
<button onClick={() => validate(mockFn)} />
</>
)}
</ValidatorProvider>
);

provider.find('button').simulate('click');
await tick();
expect(mockFn).toHaveBeenCalled()
})
})
19 changes: 19 additions & 0 deletions __tests__/Validator.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Rule } from '../src/Rule';
import { Validator } from '../src';

describe('test validator', () => {
it('should add custom rules', () => {
const rule: Rule = {
passed(): boolean {
return true;
},
message(): string {
return '';
}
}

Validator.extend('testRule', rule);

expect(Validator.hasRule('testRule')).toBeTruthy();
});
});
45 changes: 45 additions & 0 deletions __tests__/rules/required.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from 'react';
import { mount } from 'enzyme';
import { ValidatorArea } from '../../src';
import { ValidatorAreaProps } from '../../src/ValidatorArea';
import required from '../../src/rules/required';

describe('test required rule', () => {
it('should validate input', () => {
const area = mount<ValidatorArea, ValidatorAreaProps>(
<ValidatorArea rules={[required]}>
<input name="test" />
</ValidatorArea>
);

area.find('input').simulate('blur');
expect(area.state().errors.length).toBe(1);
expect(area.state().errors[0]).toBe('This field is required');
});

it('should validate textarea', () => {
const area = mount<ValidatorArea, ValidatorAreaProps>(
<ValidatorArea rules={[required]}>
<textarea name="test" />
</ValidatorArea>
);

area.find('textarea').simulate('blur');
expect(area.state().errors.length).toBe(1);
expect(area.state().errors[0]).toBe('This field is required');
});

it('should validate select', () => {
const area = mount<ValidatorArea, ValidatorAreaProps>(
<ValidatorArea rules={[required]}>
<select name="test">
<option value="">Choose...</option>
</select>
</ValidatorArea>
);

area.find('select').simulate('blur');
expect(area.state().errors.length).toBe(1);
expect(area.state().errors[0]).toBe('This field is required');
});
});
14 changes: 14 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module.exports = {
plugins: [
'@babel/plugin-proposal-class-properties'
],
presets: [
'@babel/typescript',
['@babel/env', {
targets: {
esmodules: true,
},
},],
'@babel/react',
],
};
4 changes: 4 additions & 0 deletions jest.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.[jt]sx?$",
"setupFilesAfterEnv": ["./setupTests.ts"]
}
Loading