Composition and formatResult
import validate, {arrayElements, formatResult} from 'strickland';
const withValidationErrors = (result) => {
const validationErrors = [];
function addErrorsFromObjectProps(resultObjectProps, parentPath) {
if (resultObjectProps) {
Object.keys(resultObjectProps)
// for each prop, get that prop's result
.map((propName) => ({
...resultObjectProps[propName],
propName
}))
// recursively add errors
.forEach(({propName, ...nestedResult}) => {
const propPath = [...parentPath, propName];
addErrorsFromResult({
...nestedResult,
propPath
});
});
}
}
function addErrorsFromArrayElements(resultArrayElements, parentPath) {
if (resultArrayElements) {
// recursively add errors
resultArrayElements.forEach((nestedResult, arrayElement) => {
const propPath = [...parentPath, arrayElement];
addErrorsFromResult({
...nestedResult,
propPath
});
});
}
}
function addErrorsFromResult(nestedResult) {
if (!nestedResult.isValid) {
const {
objectProps,
arrayElements,
propPath = [],
...errorResult
} = nestedResult;
// omit the `objectProps` and `arrayElements`
// result props but include `propPath`
validationErrors.push({propPath, ...errorResult});
addErrorsFromObjectProps(objectProps, propPath);
addErrorsFromArrayElements(arrayElements, propPath);
}
}
addErrorsFromResult(result);
return {
...result,
validationErrors
};
};
const validateWithErrors = formatResult(withValidationErrors, {
name: required(),
addresses: [required(), minLength(1), arrayElements({
addressType: required(),
street: [required(), {
number: required(),
name: required()
}],
city: [required()],
state: [required(), length(2, 2)],
postal: [required(), length(5, 5)]
})]
});
const data = {
name: 'Marty',
addresses: [
{
addressType: 'Home',
street: {
number: 9303,
name: 'Lyon Drive'
},
city: 'Hill Valley',
state: 'CA'
},
{
addressType: 'Work'
}
]
};
const result = validate(validateWithErrors, data);
expect(result).toMatchObject({
validationErrors: [
expect.objectContaining({
value: expect.objectContaining({
name: 'Marty',
addresses: expect.any(Array)
})
}),
expect.objectContaining({
propPath: ['addresses']
}),
expect.objectContaining({
propPath: ['addresses', 0],
value: expect.objectContaining({
addressType: 'Home'
})
}),
expect.objectContaining({
propPath: ['addresses', 0, 'postal'],
required: true
}),
expect.objectContaining({
propPath: ['addresses', 1],
value: expect.objectContaining({
addressType: 'Work'
})
}),
expect.objectContaining({
propPath: ['addresses', 1, 'street'],
required: true
}),
expect.objectContaining({
propPath: ['addresses', 1, 'city'],
required: true
}),
expect.objectContaining({
propPath: ['addresses', 1, 'state'],
required: true
}),
expect.objectContaining({
propPath: ['addresses', 1, 'postal'],
required: true
})
]
});
/*
// Most result props are omitted for illustration
// of the validationErrors contents
result = {
isValid: false,
// objectProps,
// value,
validationErrors: [
{
value: {
name: 'Marty',
addresses: [/* ... */]
})
},
{
propPath: ['addresses']
},
{
propPath: ['addresses', 0],
value: {
addressType: 'Home'
}
},
{
propPath: ['addresses', 0, 'postal'],
required: true
},
{
propPath: ['addresses', 1],
value: {
addressType: 'Work'
}
},
{
propPath: ['addresses', 1, 'street'],
required: true
},
{
propPath: ['addresses', 1, 'city'],
required: true
},
{
propPath: ['addresses', 1, 'state'],
required: true
},
{
propPath: ['addresses', 1, 'postal'],
required: true
}
]
}
*/Last updated