@dbs-portal/tool-mock
Version:
API mocking toolkit using MSW for DBS Portal development workflows
358 lines • 10.7 kB
JavaScript
/**
* Validation utilities for mock data
*/
/**
* Validate data against a schema
*/
export function validateSchema(data, schema) {
const errors = [];
const fieldErrors = {};
// Validate each field
for (const fieldValidation of schema.fields) {
const fieldName = String(fieldValidation.field);
const fieldValue = data[fieldValidation.field];
const fieldErrorList = [];
// Check required
if (fieldValidation.required && (fieldValue === undefined || fieldValue === null || fieldValue === '')) {
const message = fieldValidation.requiredMessage || `${fieldName} is required`;
fieldErrorList.push(message);
errors.push(message);
}
else if (fieldValue !== undefined && fieldValue !== null) {
// Run validation rules
for (const rule of fieldValidation.rules) {
const result = rule.validate(fieldValue, data);
if (result === false) {
const message = rule.message || `${fieldName} failed ${rule.name} validation`;
fieldErrorList.push(message);
errors.push(message);
}
else if (typeof result === 'string') {
fieldErrorList.push(result);
errors.push(result);
}
}
}
if (fieldErrorList.length > 0) {
fieldErrors[fieldName] = fieldErrorList;
}
}
// Run custom validation
if (schema.customValidation) {
const customErrors = schema.customValidation(data);
if (customErrors) {
errors.push(...customErrors);
}
}
return {
valid: errors.length === 0,
errors,
fieldErrors: Object.keys(fieldErrors).length > 0 ? fieldErrors : {},
};
}
/**
* Common validation rules
*/
export const validationRules = {
/**
* Email validation rule
*/
email: () => ({
name: 'email',
validate: (value) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(value);
},
message: 'Must be a valid email address',
}),
/**
* Minimum length validation rule
*/
minLength: (min) => ({
name: 'minLength',
validate: (value) => {
return value.length >= min;
},
message: `Must be at least ${min} characters long`,
}),
/**
* Maximum length validation rule
*/
maxLength: (max) => ({
name: 'maxLength',
validate: (value) => {
return value.length <= max;
},
message: `Must be no more than ${max} characters long`,
}),
/**
* Minimum value validation rule
*/
min: (min) => ({
name: 'min',
validate: (value) => {
return value >= min;
},
message: `Must be at least ${min}`,
}),
/**
* Maximum value validation rule
*/
max: (max) => ({
name: 'max',
validate: (value) => {
return value <= max;
},
message: `Must be no more than ${max}`,
}),
/**
* Pattern validation rule
*/
pattern: (pattern, message) => ({
name: 'pattern',
validate: (value) => {
return pattern.test(value);
},
message: message || 'Invalid format',
}),
/**
* URL validation rule
*/
url: () => ({
name: 'url',
validate: (value) => {
try {
new URL(value);
return true;
}
catch {
return false;
}
},
message: 'Must be a valid URL',
}),
/**
* Phone number validation rule
*/
phone: () => ({
name: 'phone',
validate: (value) => {
const phoneRegex = /^[\+]?[1-9][\d]{0,15}$/;
return phoneRegex.test(value.replace(/[\s\-\(\)]/g, ''));
},
message: 'Must be a valid phone number',
}),
/**
* Date validation rule
*/
date: () => ({
name: 'date',
validate: (value) => {
const date = new Date(value);
return !isNaN(date.getTime());
},
message: 'Must be a valid date',
}),
/**
* Future date validation rule
*/
futureDate: () => ({
name: 'futureDate',
validate: (value) => {
const date = new Date(value);
return date > new Date();
},
message: 'Must be a future date',
}),
/**
* Past date validation rule
*/
pastDate: () => ({
name: 'pastDate',
validate: (value) => {
const date = new Date(value);
return date < new Date();
},
message: 'Must be a past date',
}),
/**
* One of validation rule
*/
oneOf: (values) => ({
name: 'oneOf',
validate: (value) => {
return values.includes(value);
},
message: `Must be one of: ${values.join(', ')}`,
}),
/**
* Array validation rule
*/
array: (minLength, maxLength) => ({
name: 'array',
validate: (value) => {
if (!Array.isArray(value))
return false;
if (minLength !== undefined && value.length < minLength)
return false;
if (maxLength !== undefined && value.length > maxLength)
return false;
return true;
},
message: `Must be an array${minLength !== undefined ? ` with at least ${minLength} items` : ''}${maxLength !== undefined ? ` and no more than ${maxLength} items` : ''}`,
}),
/**
* Object validation rule
*/
object: () => ({
name: 'object',
validate: (value) => {
return typeof value === 'object' && value !== null && !Array.isArray(value);
},
message: 'Must be an object',
}),
/**
* Number validation rule
*/
number: () => ({
name: 'number',
validate: (value) => {
return typeof value === 'number' && !isNaN(value);
},
message: 'Must be a number',
}),
/**
* Integer validation rule
*/
integer: () => ({
name: 'integer',
validate: (value) => {
return Number.isInteger(value);
},
message: 'Must be an integer',
}),
/**
* Boolean validation rule
*/
boolean: () => ({
name: 'boolean',
validate: (value) => {
return typeof value === 'boolean';
},
message: 'Must be a boolean',
}),
/**
* String validation rule
*/
string: () => ({
name: 'string',
validate: (value) => {
return typeof value === 'string';
},
message: 'Must be a string',
}),
/**
* Custom validation rule
*/
custom: (validator, message = 'Custom validation failed') => ({
name: 'custom',
validate: validator,
message,
}),
};
/**
* Create a field validation configuration
*/
export function createFieldValidation(field, rules, required = false, requiredMessage) {
return {
field,
rules,
required,
requiredMessage: requiredMessage || `${String(field)} is required`,
};
}
/**
* Create a schema validation configuration
*/
export function createSchemaValidation(fields, customValidation) {
return {
fields,
customValidation: customValidation || (() => null),
};
}
/**
* Quick validation for common data types
*/
export const quickValidation = {
/**
* Validate user data
*/
user: (data) => {
const schema = createSchemaValidation([
createFieldValidation('email', [validationRules.email()], true),
createFieldValidation('firstName', [validationRules.minLength(1)], true),
createFieldValidation('lastName', [validationRules.minLength(1)], true),
createFieldValidation('phone', [validationRules.phone()], false),
]);
return validateSchema(data, schema);
},
/**
* Validate product data
*/
product: (data) => {
const schema = createSchemaValidation([
createFieldValidation('name', [validationRules.minLength(1)], true),
createFieldValidation('price', [validationRules.number(), validationRules.min(0)], true),
createFieldValidation('description', [validationRules.maxLength(1000)], false),
createFieldValidation('category', [validationRules.minLength(1)], true),
]);
return validateSchema(data, schema);
},
/**
* Validate order data
*/
order: (data) => {
const schema = createSchemaValidation([
createFieldValidation('customerId', [validationRules.minLength(1)], true),
createFieldValidation('items', [validationRules.array(1)], true),
createFieldValidation('total', [validationRules.number(), validationRules.min(0)], true),
createFieldValidation('status', [validationRules.oneOf(['pending', 'processing', 'shipped', 'delivered', 'cancelled'])], true),
]);
return validateSchema(data, schema);
},
/**
* Validate address data
*/
address: (data) => {
const schema = createSchemaValidation([
createFieldValidation('street', [validationRules.minLength(1)], true),
createFieldValidation('city', [validationRules.minLength(1)], true),
createFieldValidation('state', [validationRules.minLength(1)], true),
createFieldValidation('zipCode', [validationRules.pattern(/^\d{5}(-\d{4})?$/, 'Invalid ZIP code format')], true),
createFieldValidation('country', [validationRules.minLength(2)], true),
]);
return validateSchema(data, schema);
},
};
/**
* Validate and sanitize input data
*/
export function validateAndSanitize(data, schema, sanitizers) {
// First, sanitize the data if sanitizers are provided
let sanitizedData = { ...data };
if (sanitizers) {
for (const [field, sanitizer] of Object.entries(sanitizers)) {
if (sanitizedData[field] !== undefined) {
sanitizedData[field] = sanitizer(sanitizedData[field]);
}
}
}
// Then validate
const validation = validateSchema(sanitizedData, schema);
return {
valid: validation.valid,
data: sanitizedData,
errors: validation.errors,
};
}
//# sourceMappingURL=validation.js.map