Yup validation - one field required but not both at the same time

Recently I was working with yup for form validation and I faced the following problem:

I have two fields inside my form. I want the user to provide one of them but not both at the same time. I've played a little bit with different ideas to get solution below. It is using test and when functions from yup:

import { object, string } from 'yup';

const schema = object().shape(
{
userName: string()
.test(
'notBothAtTheSameTime', // test name
'You cannot pass user name at the same time as user email', // validation message to the user
// it has to be function definition to use `this`
function(userName) {
const { userEmail } = this.parent;
if (userEmail && userName) {
return false; // when user enters both userEmail & userName do not validate form
}
return true;
}
)
.when(['userEmail'], {
is: userEmail => !userEmail,
then: string().required('User name is required'),
}),
userEmail: string()
.test(
'notBothAtTheSameTime',
'You cannot pass user email at the same time as user name',
function(userEmail) {
const { userName } = this.parent;
if (userName && userEmail) {
return false;
}
return true;
}
)
.when(['userName'], {
is: userName => !userName,
then: string().required('User email is required'),
email
[['userName', 'userEmail']] // important to add
);

If you want you can extract it to its own function via addMethod. That's all 🎉. Now user should get and error when they provide both userName & userEmail.