Why yup is trigger my tests even though previous tests are failing? - javascript

I want to validate my object with the schema using yup.
But I notice that when I wrote my own test function it's trigger anyway.
I mean I validate the age property for number, null, positive, integer value. then I want to continue with my own logic test.
So I expect to NOT enter the function unless the previous tests are valid.
I'm not sure if this is how it meant to be, but in this way I must also check for valid input in my tests function, even though I add the number, null, positive, integer checks.
So am I using the yup wrong?
What I expect form yup is not invoke the test if the previous tests are invalid.
stackblitz
import { object, string, number, date, InferType } from 'yup';
let userSchema = object({
age: number()
.nullable()
.positive()
.integer()
.test({
message: 'test message',
test: (v) => {
console.log('in test!', v);
return !!v.toPrecision();
},
}),
});
userSchema
.validate({ age: null })
.then((res) => {
console.log({ res });
})
.catch((e) => {
console.log({ e });
});

Related

Yup How to get the value of field using .when on the same field?

How to get the value of field when using when on the same field ?
email: yup
.string()
.when([], {
is: (val: string) => {
console.log('val = ', val)
return true
},
then: (schema) => {
console.log('schema = ', schema)
return schema
},
})
})
I am assuming you want to get the value of email in when condition at is location as well as at then location. then you have wrote everything correctly. just remove starting [] empty array in when condition. It you don't provide any array or string then value of current item will get at is location.
ref: React-hook-form validation when input is empty. Conflict with Yup.matches
I have updated the below as per your latest comment. I have tested this in my JavaScript file. so you might need to convert it as per TSX syntax.
email: yup
.string()
.when({
is: (val) => {
console.log('val = ', val)
return true
},
then: yup.string()
.test('testKey', 'error', (value) => console.log('value at then', value)),
})
})

Mongoose automatically change the type of the value

In mongoose.model, I have chosen the type of name to be a string and the type of age to be a number, but when I enter a number as the value of name, I don't get an error and the same thing happens when I use something like '18' as the value of age.
Here is the code:
const User = mongoose.model('User', {
name: { type: String },
age: { type: Number }
});
const me = new User({
name: 12,
age: '18'
});
me.save().then(() => console.log(me)).catch(error => console.log(error));
Mongoose casts the values to the corresponding type, if it fails a CastError is thrown, from the doc:
Before running validators, Mongoose attempts to coerce values to the
correct type. This process is called casting the document. If casting
fails for a given path, the error.errors object will contain a CastError object.
You can try this by given age the value 'aa' for example.
If you want to override this behavior you can use one of the following options:
Disable casting globally: mongoose.Number.cast(false)
Disable casting just for a given path:
age: {
type: Number,
cast: false // Disable casting just for this path
},
Use a custom function:
age: {
type: Number,
cast: v => { return typeof v === 'number' && !isNaN(v) ? Number(v) : v; } // Override casting just for this path
}

Get the value of another field for validation in Yup Schema

I am using Formik with Yup for validation and TypeScript
I have a field that needs to validate based on the value of another field.
The first field is called price and the second field is called tips. The max tip value is 10% of the what ever the price entered is.
I tried to create validation for this using the following:
tips: yup.number()
.min(0, `Minimum tip is $0`)
.max( parseFloat(yup.ref('price'))* 0.1, "Maximum tip is 10% of the price.");
however this doesn't compile because yup.ref returns a Ref. How can I get the value of the price field in this validation?
number.max cannot reference other field and calculate with it at validation.
If you want to do this, you need to implement own schema with mixed.test.
Here is a example.
tips: number()
.min(0, `Minimum tip is $0`)
.test({
name: 'max',
exclusive: false,
params: { },
message: '${path} must be less than 10% of the price',
test: function (value) {
// You can access the price field with `this.parent`.
return value <= parseFloat(this.parent.price * 0.1)
},
}),
Here is doc.
You can check and try how it works here.
I hope this will help you.
if you don't want to use this.parent to access the other properties values you can use the context.
tips: number()
.min(0, `Minimum tip is $0`)
.test(
'max',
'${path} must be less than 10% of the price',
(value, context) => value <= parseFloat(context.parent.price * 0.1),
),
// OR
tips: number()
.min(0, `Minimum tip is $0`)
.test({
name: 'max',
exclusive: false,
params: { },
message: '${path} must be less than 10% of the price',
test: (value, context) => value <= parseFloat(context.parent.price * 0.1),
}),
For referencing other field value we can use this.parent or ctx.parent in case if our value is not nested.
object({
title: string(),
price: string().test('test-name', 'test-message', (value, ctx) => {
let title = ctx.parent.title;
}),
foo: string()
.test('test-name1', 'test-message1', function (value) {
let title = this.parent.title
})
})
but if we have nested value parent is going to give parent of nested
value. in this case parent is not going to work if we want to access
very parent value. we can access parent value with ctx.from. ctx.from contains parents from bottom to top. for example:
object({
title: string(),
ourObject: object({
param1: string(),
param2: string()
.test('test-name', 'test-msg', (value, ctx) => {
let title = ctx.from[ctx.from.length - 1].value.title
})
})
})
or we can easily access any data we want with providing context to schema when validating
object({
price: string(),
foo: string()
.test('test-name', 'test-message', (value, ctx) => {
let arr = ctx.context.arr;
})
})
.validate({ price: 5, foo: 'boo' }, { context: { arr: [1, 2] } })
.then(() => ...)
.catch((err) => ...)
doc
LIKE SO
.test((val,obj)=>{
console.log(val,obj.parent.otherField)
})
val=>current field value
obj=>object of the whole schema
EXAMPLE::::
date: yup.
string()
.required()
.test('type', 'Date should be future date', (val) => {
console.log(val);
}),
time: yup
.string()
.required()
.test('type', 'Time should be future time', (val,obj) => {
console.log(val,'in time val',obj.parent.date,'date')
}),

Yup conditional object validation not working

I'm trying to define a Yup validation for an object - if a defined sibling is set to true, the field of type object should be required, otherwise not
Example:
const { object, string, number, date, boolean } = require('yup')
const contactSchema = object({
isBig: boolean(),
count: number()
.when('isBig', {
is: true, // alternatively: (val) => val == true
then: number().min(5),
otherwise: number().min(0),
}),
complexOne: object({
simpleOne: string(),
})
.when('isBig', {
is: true,
then: object().required(),
otherwise: object(),
})
})
The object passed into the validation:
{
isBig: true,
count: -1,
}
As you can see, I intentionally don't pass the complexOne since I want Yup to display the error. The validation for the count works correctly - if the value is less than 0 and the isBig is set to true, Yup will correctly display an error message ValidationError: count must be greater than or equal to 5
Unfortunately, it completely ignores the conditional validation set for the complexOne field. Either yup does not support the when for object types or I'm doing something incorrectly.
Thanks for any help
you must set strict option to true in order to only validate the object, and skip any coercion or transformation:
contactSchema.validate(contact, { strict: true })
.then(obj => {
console.log(obj)
}, err => {
console.log(err.message)
})
Demo:

mongoose - Cast to number failed for value "[object Object]" at path

I have a schema that looks like this:
module.exports = mongoose.model('Buyer',{
username: String,
password: String,
email: String,
url: String,
id: String,
firstName: String,
lastName: String,
credit: Number,
});
I am trying to add to their credit whenever they submit a Stripe payment, but when I do, I get the error that is in the title:
Cast to number failed for value "[object Object]" at path
Here is where I try to update credit:
var query = Buyer.findOne({'_id': req.user._id});
query.exec(function(err,buyer){
buyer.credit = { credit: parseInt(req.body.amount,10) };
buyer.markModified('credit');
buyer.save(function (err) {
if (!err) {
//--NO ERROR
} else {
return console.log(err);
}
});
});
I'm not sure how to add to existing credit, instead of overwriting it as I'm doing above, but I suppose I'll ask that in another question after looking further into it.
Anyways, I don't understand what is going on with my error. I tried doing it without the parseInt but noticed it was a string.
Change this line:
buyer.credit = { credit: parseInt(req.body.amount,10) };
To:
buyer.credit = req.body.amount;
You just want to set buyer.credit to the value, not another object with its own credit field. You don't need to the parseInt because Mongoose will do that for you based on your definition of credit as a Number in the schema.
You can also remove the buyer.markModified('credit'); line.

Categories

Resources