Skip to content

Commit

Permalink
optionalType and fix required schema validation
Browse files Browse the repository at this point in the history
  • Loading branch information
Mariusz Przodała committed Dec 8, 2017
1 parent e57df26 commit 3e7525e
Show file tree
Hide file tree
Showing 2 changed files with 286 additions and 2 deletions.
44 changes: 42 additions & 2 deletions src/Schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,25 @@ class Schema {
static oneOfTypes(types) {
return new OneOfTypes(types);
}
static optionalType(type, uniqueTypeName = '') {
const fieldType = getFieldType({ type });
const { name = 'Type' } = fieldType;
return new SchemaType(`Optional${name}${uniqueTypeName}`, {
getDefaultValue() {
return undefined;
},
validator(value, key, index) {
if (value === null || value === undefined) {
return true;
}
return this.validateType(type, value, key, index);
},
requiredValidator(value, key) {
this.validateRequired({ type, required: true }, value, key);
return true;
},
});
}

constructor(schema, messages, validateKeys = true) {
this.schema = schema;
Expand Down Expand Up @@ -214,6 +233,10 @@ class Schema {
this.typesRequiredValidators[name](value, key);
return;
}
if (fieldSchema.type instanceof Schema) {
this.validateRequiredTypeSchema(fieldSchema.type.schema, value, key);
return;
}
this.validateRequiredType(value, key);
}
}
Expand All @@ -226,7 +249,7 @@ class Schema {
}

validateRequiredTypeObject(value, key) {
if (typeof value === 'object' && Object.keys(value).length > 0) {
if (typeof value === 'object' && value !== null && Object.keys(value).length > 0) {
return;
}
const { label } = this.getField(key);
Expand All @@ -242,7 +265,7 @@ class Schema {
}

validateRequiredTypeNumber(value, key) {
if (isNaN(value)) {
if (typeof value !== 'number' || isNaN(value)) {
const { label } = this.getField(key);
this.setError(key, this.messages.validateRequired(label || key));
}
Expand All @@ -256,6 +279,23 @@ class Schema {
this.setError(key, this.messages.validateRequired(label || key));
}

validateRequiredTypeSchema(schema, value, key) {
if (typeof value === 'object' && value !== null) {
let hasRequiredKeys = true;
const valueKeys = Object.keys(value);
Object.keys(schema).forEach((requiredKey) => {
if (valueKeys.indexOf(requiredKey) < 0) {
hasRequiredKeys = false;
}
});
if (hasRequiredKeys) {
return;
}
}
const { label } = this.getField(key);
this.setError(key, this.messages.validateRequired(label || key));
}

validateType(type, value, key, index) {
const { name: typeName } = type;
if (type instanceof SchemaType && typeof this.typesValidators[typeName] !== 'function') {
Expand Down
244 changes: 244 additions & 0 deletions src/Schema.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,74 @@ describe('Schema', () => {
expect(Object.keys(testObject2Errors).length).toBe(0);
expect(Object.keys(testObject3Errors).length).toBe(1);
});

it('should validate optionalType', () => {
const subSchema = new Schema({
name: {
type: Schema.optionalType(String),
},
});
const schema = new Schema({
name: {
type: Schema.optionalType(String),
},
age: {
type: Schema.optionalType(Number),
},
date: {
type: Schema.optionalType(Date),
},
object: {
type: Schema.optionalType(Object),
},
isTrue: {
type: Schema.optionalType(Boolean),
},
array: {
type: Schema.optionalType(Array),
},
schemaObject: {
type: Schema.optionalType(subSchema),
},
});
const modelWithEmptyCorrectTypes = {
name: '',
age: NaN,
date: new Date(),
object: {},
isTrue: false,
array: [],
schemaObject: {},
};
const modelWithNull = {
name: null,
age: null,
date: null,
object: null,
isTrue: null,
array: null,
schemaObject: null,
};
const modelWithUndefined = {};
const modelWithNumber = {
name: 0,
age: 'foo',
date: 'foo',
object: 'foo',
isTrue: 'foo',
array: 0,
schemaObject: 'foo',
};

const modelWithEmptyCorrectTypesErrors = schema.validate(modelWithEmptyCorrectTypes);
const modelWithNullErrors = schema.validate(modelWithNull);
const modelWithUndefinedErrors = schema.validate(modelWithUndefined);
const modelWithNumberErrors = schema.validate(modelWithNumber);
expect(Object.keys(modelWithEmptyCorrectTypesErrors).length).toBe(0);
expect(Object.keys(modelWithNullErrors).length).toBe(0);
expect(Object.keys(modelWithUndefinedErrors).length).toBe(0);
expect(Object.keys(modelWithNumberErrors).length).toBe(7);
});
});

describe('Validation array of types', () => {
Expand Down Expand Up @@ -496,6 +564,82 @@ describe('Schema', () => {
expect(Object.keys(testObject3Errors).length).toBe(1);
expect(testObject3Errors.owners[0].name.length).toBe(1);
});

it('should validate array of optionalType', () => {
const subSchema = new Schema({
name: {
type: Schema.optionalType(String),
},
});
const schema = new Schema({
name: {
type: [Schema.optionalType(String)],
},
age: {
type: [Schema.optionalType(Number)],
},
date: {
type: [Schema.optionalType(Date)],
},
object: {
type: [Schema.optionalType(Object)],
},
isTrue: {
type: [Schema.optionalType(Boolean)],
},
array: {
type: [Schema.optionalType(Array)],
},
schemaObject: {
type: [Schema.optionalType(subSchema)],
},
});
const modelWithEmptyCorrectTypes = {
name: [''],
age: [NaN],
date: [new Date()],
object: [{}],
isTrue: [false],
array: [[]],
schemaObject: [{}],
};
const modelWithNull = {
name: [null],
age: [null],
date: [null],
object: [null],
isTrue: [null],
array: [null],
schemaObject: [null],
};
const modelWithUndefined = {
name: [],
age: [],
date: [],
object: [],
isTrue: [],
array: [],
schemaObject: [],
};
const modelWithNumber = {
name: [0],
age: ['foo'],
date: ['foo'],
object: ['foo'],
isTrue: ['foo'],
array: [0],
schemaObject: ['foo'],
};

const modelWithEmptyCorrectTypesErrors = schema.validate(modelWithEmptyCorrectTypes);
const modelWithNullErrors = schema.validate(modelWithNull);
const modelWithUndefinedErrors = schema.validate(modelWithUndefined);
const modelWithNumberErrors = schema.validate(modelWithNumber);
expect(Object.keys(modelWithEmptyCorrectTypesErrors).length).toBe(0);
expect(Object.keys(modelWithNullErrors).length).toBe(0);
expect(Object.keys(modelWithUndefinedErrors).length).toBe(0);
expect(Object.keys(modelWithNumberErrors).length).toBe(7);
});
});

describe('Validate required', () => {
Expand Down Expand Up @@ -628,6 +772,92 @@ describe('Schema', () => {
expect(Object.keys(testObjectErrors).length).toBe(0);
expect(Object.keys(testObject2Errors).length).toBe(1);
});

it('should validate optionalType', () => {
const subSchema = new Schema({
name: {
type: Schema.optionalType(String),
},
});
const schema = new Schema({
name: {
type: Schema.optionalType(String),
required: true,
},
age: {
type: Schema.optionalType(Number),
required: true,
},
date: {
type: Schema.optionalType(Date),
required: true,
},
object: {
type: Schema.optionalType(Object),
required: true,
},
isTrue: {
type: Schema.optionalType(Boolean),
required: true,
},
array: {
type: Schema.optionalType(Array),
required: true,
},
schemaObject: {
type: Schema.optionalType(subSchema),
required: true,
},
});
const modelWithFilledCorrectTypes = {
name: 'foo',
age: 123,
date: new Date(),
object: { foo: 'bar' },
isTrue: true,
array: ['foo'],
schemaObject: { name: '' },
};
const modelWithEmptyCorrectTypes = {
name: '',
age: NaN,
date: new Date(),
object: {},
isTrue: false,
array: [],
schemaObject: {},
};
const modelWithNull = {
name: null,
age: null,
date: null,
object: null,
isTrue: null,
array: null,
schemaObject: null,
};
const modelWithUndefined = {};
const modelWithNumber = {
name: 0,
age: 'foo',
date: 'foo',
object: 'foo',
isTrue: 'foo',
array: 0,
schemaObject: 'foo',
};

const modelWithFilledCorrectTypesErrors = schema.validate(modelWithFilledCorrectTypes);
const modelWithEmptyCorrectTypesErrors = schema.validate(modelWithEmptyCorrectTypes);
const modelWithNullErrors = schema.validate(modelWithNull);
const modelWithUndefinedErrors = schema.validate(modelWithUndefined);
const modelWithNumberErrors = schema.validate(modelWithNumber);
expect(Object.keys(modelWithFilledCorrectTypesErrors).length).toBe(0);
expect(Object.keys(modelWithEmptyCorrectTypesErrors).length).toBe(6);
expect(Object.keys(modelWithNullErrors).length).toBe(7);
expect(Object.keys(modelWithUndefinedErrors).length).toBe(7);
expect(Object.keys(modelWithNumberErrors).length).toBe(7);
});
});
describe('Should validate using custom validators', () => {
jest.useFakeTimers();
Expand Down Expand Up @@ -859,6 +1089,20 @@ describe('Schema', () => {
});
});

it('should get default value for optionalType', () => {
const schema = new Schema({
foo: {
type: Schema.optionalType(String),
},
bar: {
type: Schema.optionalType(String),
defaultValue: 'foo',
},
});
const defaultModel = schema.getDefaultValues();
expect(defaultModel).toEqual({ bar: 'foo' });
});

it('should not return default value if field has flag disableDefaultValue', () => {
const schema = new Schema({
foo: {
Expand Down

0 comments on commit 3e7525e

Please sign in to comment.