Skip to content

Commit 0817594

Browse files
author
David Tang
committed
first implementation on validateSometimes
1 parent 37d63bb commit 0817594

File tree

6 files changed

+213
-3
lines changed

6 files changed

+213
-3
lines changed

README.md

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,63 @@
11
# ember-changeset-conditional-validations
22

3-
This README outlines the details of collaborating on this Ember addon.
3+
An extra validator for conditional validations with [`ember-changeset-validations`](https://github.com/DockYard/ember-changeset-validations).
4+
5+
## Installation
6+
7+
```
8+
ember install ember-changeset-conditional-validations
9+
```
10+
11+
## Usage
12+
13+
Let's say you want to validate a user's settings. Only if the payment method is a credit card should the credit card number validations be applied.
14+
15+
```js
16+
import Ember from 'ember';
17+
import { validatePresence, validateNumber } from 'ember-changeset-validations/validators';
18+
import validateSometimes from 'ember-changeset-conditional-validations/validators/sometimes';
19+
20+
const { get } = Ember;
21+
22+
const Validations = {
23+
creditCardNumber: validateSometimes([
24+
validatePresence(true),
25+
validateNumber({ is: 16 })
26+
], function(changes) {
27+
return get(changes, 'paymentMethod.isCreditCard')
28+
})
29+
};
30+
```
31+
32+
`validateSometimes` takes 2 arguments. The first is a list of validators and the second is a callback function which represents the condition. If the condition callback returns `true`, the rules will be added.
33+
34+
```js
35+
import Changeset from 'ember-changeset';
36+
import lookupValidator from 'ember-changeset-validations';
37+
38+
let settings = {};
39+
let changeset = new Changeset(settings, lookupValidator(Validations), Validations);
40+
41+
console.log(changeset.get('isValid')); // true
42+
changeset.set('paymentMethod', {
43+
isCreditCard: true
44+
});
45+
changeset.validate();
46+
console.log(changeset.get('isValid')); // false
47+
console.log(changeset.get('errors')); // [{key: 'creditCardNumber', validation: ['Credit card number can't be blank', 'Credit card number must be a number']}]
48+
changeset.set('creditCardNumber', '1234567890123456');
49+
changeset.validate();
50+
console.log(changeset.get('isValid')); // true
51+
changeset.set('creditCardNumber', '1234');
52+
changeset.validate();
53+
console.log(changeset.get('isValid')); // false
54+
console.log(changeset.get('errors')); // [{key: 'creditCardNumber', value: '1234', validation: ['Credit card number must be equal to 16']}]
55+
changeset.set('paymentMethod', {
56+
isCreditCard: false
57+
});
58+
changeset.validate();
59+
console.log(changeset.get('isValid')); // true
60+
```
461

562
## Installation
663

addon/validators/sometimes.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export default function validateSometimes(validators, condition) {
2+
return validators.map(function(validator) {
3+
return function(key, newValue, oldValue, changes, content) {
4+
if (condition(changes)) {
5+
return validator(key, newValue, oldValue, changes, content);
6+
}
7+
return true;
8+
};
9+
});
10+
}

package.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
{
22
"name": "ember-changeset-conditional-validations",
33
"version": "0.0.0",
4-
"description": "The default blueprint for ember-cli addons.",
4+
"description": "Conditional validations for ember-changeset-validations",
55
"keywords": [
6-
"ember-addon"
6+
"ember-addon",
7+
"ember-changeset",
8+
"ember-changeset-validations",
9+
"changeset",
10+
"validations",
11+
"conditional validations"
712
],
813
"license": "MIT",
914
"author": "",
@@ -23,6 +28,8 @@
2328
"devDependencies": {
2429
"broccoli-asset-rev": "^2.4.5",
2530
"ember-ajax": "^3.0.0",
31+
"ember-changeset": "^1.3.0",
32+
"ember-changeset-validations": "^1.2.8",
2633
"ember-cli": "~2.14.1",
2734
"ember-cli-dependency-checker": "^1.3.0",
2835
"ember-cli-eslint": "^3.0.0",
@@ -37,6 +44,7 @@
3744
"ember-export-application-global": "^2.0.0",
3845
"ember-load-initializers": "^1.0.0",
3946
"ember-resolver": "^4.0.0",
47+
"ember-sinon": "^0.7.0",
4048
"ember-source": "~2.14.1",
4149
"loader.js": "^4.2.3"
4250
},
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/* eslint no-console: "off" */
2+
import Ember from 'ember';
3+
import Changeset from 'ember-changeset';
4+
import lookupValidator from 'ember-changeset-validations';
5+
import { validatePresence, validateNumber } from 'ember-changeset-validations/validators';
6+
import validateSometimes from 'ember-changeset-conditional-validations/validators/sometimes';
7+
8+
const { get } = Ember;
9+
10+
const Validations = {
11+
creditCardNumber: validateSometimes([
12+
validatePresence(true),
13+
validateNumber({ is: 16 })
14+
], function(changes) {
15+
return get(changes, 'paymentMethod.isCreditCard')
16+
})
17+
};
18+
19+
export default Ember.Route.extend({
20+
model() {
21+
let settings = {};
22+
let changeset = new Changeset(settings, lookupValidator(Validations), Validations);
23+
24+
console.log(changeset.get('isValid')); // true
25+
changeset.set('paymentMethod', {
26+
isCreditCard: true
27+
});
28+
changeset.validate();
29+
console.log(changeset.get('isValid')); // false
30+
console.log(changeset.get('errors')); // [{key: 'creditCardNumber', validation: ['Credit card number can't be blank', 'Credit card number must be a number']}]
31+
changeset.set('creditCardNumber', '1234567890123456');
32+
changeset.validate();
33+
console.log(changeset.get('isValid')); // true
34+
changeset.set('creditCardNumber', '1234');
35+
changeset.validate();
36+
console.log(changeset.get('isValid')); // false
37+
console.log(changeset.get('errors')); // [{key: 'creditCardNumber', value: '1234', validation: ['Credit card number must be equal to 16']}]
38+
changeset.set('paymentMethod', {
39+
isCreditCard: false
40+
});
41+
changeset.validate();
42+
console.log(changeset.get('isValid')); // true
43+
}
44+
});

tests/helpers/is-function.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import Ember from 'ember';
2+
3+
const { typeOf } = Ember;
4+
5+
export default function isFunction(fn) {
6+
return typeOf(fn) === 'function';
7+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { module, test } from 'qunit';
2+
import validateSometimes from 'ember-changeset-conditional-validations/validators/sometimes';
3+
import sinon from 'sinon';
4+
import isFunction from './../../helpers/is-function';
5+
6+
module('Unit | Validator | sometimes');
7+
8+
test('an array of validators is returned', function(assert) {
9+
let validatorA = sinon.stub();
10+
let validatorB = sinon.stub();
11+
let condition = sinon.stub();
12+
let validators = validateSometimes([validatorA, validatorB], condition);
13+
assert.equal(validators.length, 2);
14+
assert.ok(validators.every(isFunction));
15+
});
16+
17+
test('if the condition returns false, the validators return true', function(assert) {
18+
let validatorA = sinon.stub();
19+
let validatorB = sinon.stub();
20+
let condition = sinon.stub().returns(false);
21+
let validators = validateSometimes([validatorA, validatorB], condition);
22+
let validations = validators.map((validator) => {
23+
return validator();
24+
});
25+
assert.deepEqual(validations, [true, true]);
26+
});
27+
28+
test('if the condition returns true, the validators are invoked and their result is returned', function(assert) {
29+
let validatorA = sinon.stub().returns('Error message A');
30+
let validatorB = sinon.stub().returns(true);
31+
let condition = sinon.stub().returns(true);
32+
let validators = validateSometimes([validatorA, validatorB], condition);
33+
let validations = validators.map((validator) => {
34+
return validator();
35+
});
36+
assert.deepEqual(validations, ['Error message A', true]);
37+
});
38+
39+
test('the condition is invoked with the changes for each validator', function(assert) {
40+
let key = 'name';
41+
let newValue = 'Yehuda';
42+
let oldValue = 'YK';
43+
let changes = {};
44+
45+
let validatorA = sinon.stub().returns('Error message A');
46+
let validatorB = sinon.stub().returns(true);
47+
let condition = sinon.spy();
48+
49+
let validators = validateSometimes([validatorA, validatorB], condition);
50+
validators.map((validator) => {
51+
return validator(key, newValue, oldValue, changes);
52+
});
53+
assert.equal(condition.callCount, 2);
54+
assert.strictEqual(condition.getCall(0).args[0], changes);
55+
assert.strictEqual(condition.getCall(1).args[0], changes);
56+
});
57+
58+
test('each validator is invoked with key, newValue, oldValue, changes, and content', function(assert) {
59+
let key = 'name';
60+
let newValue = 'Yehuda';
61+
let oldValue = 'YK';
62+
let changes = {};
63+
let content = {};
64+
65+
let validatorA = sinon.spy();
66+
let validatorB = sinon.spy();
67+
let condition = sinon.stub().returns(true);
68+
let validators = validateSometimes([validatorA, validatorB], condition);
69+
70+
validators.map((validator) => {
71+
return validator(key, newValue, oldValue, changes, content);
72+
});
73+
74+
assert.strictEqual(validatorA.firstCall.args[0], key);
75+
assert.strictEqual(validatorA.firstCall.args[1], newValue);
76+
assert.strictEqual(validatorA.firstCall.args[2], oldValue);
77+
assert.strictEqual(validatorA.firstCall.args[3], changes);
78+
assert.strictEqual(validatorA.firstCall.args[4], content);
79+
assert.strictEqual(validatorB.firstCall.args[0], key);
80+
assert.strictEqual(validatorB.firstCall.args[1], newValue);
81+
assert.strictEqual(validatorB.firstCall.args[2], oldValue);
82+
assert.strictEqual(validatorB.firstCall.args[3], changes);
83+
assert.strictEqual(validatorB.firstCall.args[4], content);
84+
});

0 commit comments

Comments
 (0)