Skip to content

Commit 061e590

Browse files
committed
fix tests and add ref support to oneOf/notOneOf
1 parent 77a3a33 commit 061e590

10 files changed

+120
-109
lines changed

.babelrc

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
],
1010
"env": {
1111
"test": {
12+
"sourceMaps": "inline",
1213
"presets": [
1314
[
1415
"jason",

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,13 @@ json separate from validating it, via the `cast` method.
101101
npm install -S yup
102102
```
103103

104-
Yup always relies on the `Promise` global object to handle asynchronous values as well `Set`.
104+
Yup always relies on the `Promise` global object to handle asynchronous values as well as `Set` and `Map`.
105105
For browsers that do not support these, you'll need to include a polyfill, such as core-js:
106106

107107
```js
108108
import 'core-js/es6/promise';
109109
import 'core-js/es6/set';
110+
import 'core-js/es6/map';
110111
```
111112

112113
## Usage

src/Reference.js

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ export default class Reference {
1010
return !!(value && (value.__isYupRef || value instanceof Reference))
1111
}
1212

13+
toString() {
14+
return `Ref(${this.key})`
15+
}
16+
1317
constructor(key, mapFn, options = {}) {
1418
validateName(key)
1519
let prefix = options.contextPrefix || '$';

src/index.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ import reach from './util/reach';
1212
import isSchema from './util/isSchema';
1313

1414
let boolean = bool;
15-
let ref =(key, options) => new Ref(key, options);
15+
let ref = (key, options) => new Ref(key, options);
1616

17-
let lazy =(fn) => new Lazy(fn);
17+
let lazy = (fn) => new Lazy(fn);
1818

1919
function addMethod(schemaType, name, fn) {
2020
if (!schemaType || !isSchema(schemaType.prototype))

src/mixed.js

+41-16
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,29 @@ function extractTestParams(name, message, test) {
3434
return opts
3535
}
3636

37+
const listToArray = list =>
38+
toArray(list).concat(toArray(list.refs.values()))
39+
40+
const removeFromList = (value, list) => {
41+
Ref.isRef(value) ? list.refs.delete(value.key) : list.delete(value)
42+
}
43+
44+
const addToList = (value, list) => {
45+
Ref.isRef(value) ? list.refs.set(value.key, value) : list.add(value)
46+
}
47+
48+
const hasInList = (value, resolve, list) => {
49+
if (list.has(value)) return true;
50+
51+
let item; let values = list.refs.values()
52+
53+
while (item = values.next(), !item.done) {
54+
if (resolve(item.value) === value)
55+
return true;
56+
}
57+
return false
58+
}
59+
3760
export default function SchemaType(options = {}){
3861
if ( !(this instanceof SchemaType))
3962
return new SchemaType()
@@ -42,8 +65,12 @@ export default function SchemaType(options = {}){
4265
this._conditions = []
4366
this._options = { abortEarly: true, recursive: true }
4467
this._exclusive = Object.create(null)
68+
4569
this._whitelist = new Set()
70+
this._whitelist.refs = new Map()
71+
4672
this._blacklist = new Set()
73+
this._blacklist.refs = new Map()
4774
this.tests = []
4875
this.transforms = []
4976

@@ -380,23 +407,22 @@ SchemaType.prototype = {
380407
var next = this.clone();
381408

382409
enums.forEach(val => {
383-
if (next._blacklist.has(val))
384-
next._blacklist.delete(val)
385-
next._whitelist.add(val)
410+
addToList(val, next._whitelist)
411+
removeFromList(val, next._blacklist)
386412
})
387413

388414
next._whitelistError = createValidation({
389415
message,
390416
name: 'oneOf',
391417
test(value) {
418+
if (value === undefined) return true
392419
let valids = this.schema._whitelist
393-
if (valids.size && !(value === undefined || valids.has(value)))
394-
return this.createError({
395-
params: {
396-
values: toArray(valids).join(', ')
397-
}
398-
})
399-
return true
420+
421+
return hasInList(value, this.resolve, valids) ? true : this.createError({
422+
params: {
423+
values: listToArray(valids).join(', ')
424+
}
425+
})
400426
}
401427
})
402428

@@ -405,21 +431,20 @@ SchemaType.prototype = {
405431

406432
notOneOf(enums, message = locale.notOneOf) {
407433
var next = this.clone();
408-
409-
enums.forEach( val => {
410-
next._whitelist.delete(val)
411-
next._blacklist.add(val)
434+
enums.forEach(val => {
435+
addToList(val, next._blacklist)
436+
removeFromList(val, next._whitelist)
412437
})
413438

414439
next._blacklistError = createValidation({
415440
message,
416441
name: 'notOneOf',
417442
test(value) {
418443
let invalids = this.schema._blacklist
419-
if (invalids.size && invalids.has(value))
444+
if (hasInList(value, this.resolve, invalids))
420445
return this.createError({
421446
params: {
422-
values: toArray(invalids).join(', ')
447+
values: listToArray(invalids).join(', ')
423448
}
424449
})
425450
return true

src/number.js

+4-16
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ inherits(NumberSchema, MixedSchema, {
5858
})
5959
},
6060

61-
less(less, msg) {
61+
lessThan(less, msg) {
6262
return this.test({
63-
name: 'less',
63+
name: 'max',
6464
exclusive: true,
6565
params: { less },
6666
message: msg || locale.less,
@@ -70,9 +70,9 @@ inherits(NumberSchema, MixedSchema, {
7070
})
7171
},
7272

73-
more(more, msg) {
73+
moreThan(more, msg) {
7474
return this.test({
75-
name: 'more',
75+
name: 'min',
7676
exclusive: true,
7777
params: { more },
7878
message: msg || locale.more,
@@ -82,18 +82,6 @@ inherits(NumberSchema, MixedSchema, {
8282
})
8383
},
8484

85-
notEqual(notEqual, msg) {
86-
return this.test({
87-
name: 'notEqual',
88-
exclusive: true,
89-
params: { notEqual },
90-
message: msg || locale.notEqual,
91-
test(value) {
92-
return isAbsent(value) || value !== this.resolve(notEqual)
93-
}
94-
})
95-
},
96-
9785
positive(msg) {
9886
return this.min(0, msg || locale.positive)
9987
},

src/util/createValidation.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,16 @@ export default function createValidation(options) {
5252

5353
function validate({ value, path, label, options, originalValue, sync, ...rest }) {
5454
let parent = options.parent;
55-
var resolve = (value) => Ref.isRef(value)
55+
let resolve = (value) => Ref.isRef(value)
5656
? value.getValue(parent, options.context)
5757
: value
5858

59-
var createError = createErrorFactory({
59+
let createError = createErrorFactory({
6060
message, path, value, originalValue, params
6161
, label, resolve, name
6262
})
6363

64-
var ctx = { path, parent, type: name, createError, resolve, options, ...rest }
64+
let ctx = { path, parent, type: name, createError, resolve, options, ...rest }
6565

6666
return runTest(test, ctx, value, sync)
6767
.then(validOrError => {

test/helpers.js

+13-7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import printValue from '../src/util/printValue';
12

23
export let castAndShouldFail = (schema, value) => {
34
(()=> schema.cast(value))
@@ -9,7 +10,7 @@ export let castAndShouldFail = (schema, value) => {
910

1011
export let castAll = (inst, { invalid = [], valid = [] }) => {
1112
valid.forEach(([value, result, schema = inst ]) => {
12-
it(`should cast ${JSON.stringify(value)} to ${JSON.stringify(result)}`, () => {
13+
it(`should cast ${printValue(value)} to ${printValue(result)}`, () => {
1314
expect(
1415
schema.cast(value)
1516
)
@@ -18,24 +19,29 @@ export let castAll = (inst, { invalid = [], valid = [] }) => {
1819
})
1920

2021
invalid.forEach((value) => {
21-
it(`should not cast ${JSON.stringify(value)}`, () => {
22+
it(`should not cast ${printValue(value)}`, () => {
2223
castAndShouldFail(inst, value)
2324
})
2425
})
2526
}
2627

2728
export let validateAll = (inst, { valid = [], invalid = [] }) => {
28-
runValidations(valid, true)
29-
runValidations(invalid, false)
29+
describe('valid:', () => {
30+
runValidations(valid, true)
31+
})
32+
33+
describe('invalid:', () => {
34+
runValidations(invalid, false)
35+
})
3036

3137
function runValidations(arr, isValid) {
3238
arr.forEach((config) => {
33-
let value = config, schema = inst;
39+
let message = '', value = config, schema = inst;
3440

3541
if (Array.isArray(config))
36-
[ value, schema ] = config;
42+
[ value, schema, message = '' ] = config;
3743

38-
it(`${JSON.stringify(value)} should be ${isValid ? 'valid' : 'invalid'}`,
44+
it(`${printValue(value)}${message && ` (${message})`}`,
3945
() => schema.isValid(value).should.become(isValid)
4046
)
4147
})

test/mixed.js

+45-42
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
1-
import mixed from '../src/mixed';
2-
import object from '../src/object';
3-
import string from '../src/string';
4-
import number from '../src/number';
5-
import reach from '../src/util/reach';
6-
1+
import {mixed, string, number, object, ref, reach } from '../src';
72
let noop = () => {}
83

94
function ensureSync(fn) {
@@ -136,48 +131,56 @@ describe('Mixed Types ', () => {
136131
})
137132
})
138133

139-
it('should ignore absent values', () => {
140-
return Promise.all([
141-
mixed()
142-
.oneOf(['hello'])
143-
.isValid(undefined)
144-
.should.eventually().equal(true),
145-
mixed()
146-
.nullable()
147-
.oneOf(['hello'])
148-
.isValid(null)
149-
.should.eventually().equal(false),
150-
mixed()
151-
.oneOf(['hello'])
152-
.required()
153-
.isValid(undefined)
154-
.should.eventually().equal(false),
155-
mixed()
156-
.nullable()
157-
.oneOf(['hello'])
158-
.required()
159-
.isValid(null)
160-
.should.eventually().equal(false)
161-
])
134+
describe('oneOf', () => {
135+
let inst = mixed().oneOf(['hello'])
136+
137+
TestHelpers.validateAll(inst, {
138+
valid: [
139+
undefined,
140+
'hello'
141+
],
142+
invalid: [
143+
'YOLO',
144+
[undefined, inst.required(), 'required'],
145+
[null, inst.nullable()],
146+
[null, inst.nullable().required(), 'required'],
147+
]
148+
})
149+
150+
it('should work with refs', async () => {
151+
let inst = object({
152+
foo: string(),
153+
bar: string().oneOf([ref('foo'), 'b'])
154+
})
155+
156+
await inst.validate({ foo: 'a', bar: 'a' }).should.be.fulfilled()
157+
158+
await inst.validate({ foo: 'foo', bar: 'bar' }).should.be.rejected()
159+
})
162160
})
163161

164-
it('should exclude values', () => {
162+
describe('should exclude values', () => {
165163
let inst = mixed().notOneOf([5, 'hello'])
166164

167-
return Promise.all([
168-
inst.isValid(6).should.eventually().equal(true),
169-
inst.isValid('hfhfh').should.eventually().equal(true),
170-
171-
inst.isValid(5).should.eventually().equal(false),
165+
TestHelpers.validateAll(inst, {
166+
valid: [
167+
6,
168+
'hfhfh',
169+
[5, inst.oneOf([5]), '`oneOf` called after'],
170+
null,
171+
],
172+
invalid: [
173+
5,
174+
[null, inst.required(), 'required schema']
175+
]
176+
})
172177

173-
inst.validate(5).should.be.rejected().then(err => {
174-
err.errors[0].should.equal('this must not be one of the following values: 5, hello')
175-
}),
176-
inst.oneOf([5]).isValid(5).should.eventually().equal(true),
178+
it('should throw the correct error', async () => {
179+
let err = await inst.validate(5).should.be.rejected()
177180

178-
inst.isValid(null).should.eventually().equal(true),
179-
inst.required().isValid(null).should.eventually().equal(false)
180-
])
181+
err.errors[0].should
182+
.equal('this must not be one of the following values: 5, hello')
183+
})
181184
})
182185

183186
it('should run subset of validations first', () => {

0 commit comments

Comments
 (0)