Advanced validation using regular expressions, type checking, and custom validator functions.
Asserts that the reference value is an instance of the provided constructor function.
/**
* Asserts that the reference value is instance of provided type
* @param {Function} type - Constructor function to check against
* @returns {Assertion} Assertion chain object for further chaining
*/
instanceof(type)Alias:
instanceOf()Usage Examples:
const Code = require('@hapi/code');
const expect = Code.expect;
// Built-in types
expect(new Date()).to.be.an.instanceof(Date);
expect([1, 2, 3]).to.be.an.instanceof(Array);
expect(/abc/).to.be.an.instanceof(RegExp);
expect(new Error('test')).to.be.an.instanceof(Error);
// Custom classes
class User {
constructor(name) {
this.name = name;
}
}
const user = new User('John');
expect(user).to.be.an.instanceof(User);
expect(user).to.be.an.instanceOf(Object); // inheritance chain
// Error classes
class CustomError extends Error {
constructor(message) {
super(message);
this.name = 'CustomError';
}
}
const err = new CustomError('Something went wrong');
expect(err).to.be.an.instanceof(CustomError);
expect(err).to.be.an.instanceof(Error); // Parent classAsserts that the reference value's string representation matches the provided regular expression.
/**
* Asserts that the reference value matches the regex pattern
* @param {RegExp} regex - Regular expression pattern to match
* @returns {Assertion} Assertion chain object for further chaining
*/
match(regex)Alias:
matches()Usage Examples:
// String pattern matching
expect('hello123').to.match(/hello\d+/);
expect('user@example.com').to.match(/\w+@\w+\.\w+/);
expect('2023-12-01').to.match(/^\d{4}-\d{2}-\d{2}$/);
// Case insensitive matching
expect('Hello World').to.match(/hello world/i);
// Phone number validation
expect('(555) 123-4567').to.match(/^\(\d{3}\) \d{3}-\d{4}$/);
// Using alias
expect('abc123').to.matches(/[a-z]+\d+/);
// Non-string values (converted to string)
expect(12345).to.match(/^\d+$/);
expect(true).to.match(/^true$/);Asserts that the reference value satisfies the provided validator function.
/**
* Asserts that the reference value satisfies validator function
* @param {Function} validator - Function that returns boolean
* @returns {Assertion} Assertion chain object for further chaining
*/
satisfy(validator)Alias:
satisfies()Usage Examples:
// Simple validation
expect(10).to.satisfy(x => x > 5);
expect('hello').to.satisfy(str => str.length === 5);
// Complex validation
const isValidEmail = (email) => {
return typeof email === 'string' &&
email.includes('@') &&
email.includes('.') &&
email.length > 5;
};
expect('user@example.com').to.satisfy(isValidEmail);
expect('invalid-email').to.not.satisfy(isValidEmail);
// Array validation
const isNonEmptyArray = (arr) => Array.isArray(arr) && arr.length > 0;
expect([1, 2, 3]).to.satisfy(isNonEmptyArray);
expect([]).to.not.satisfy(isNonEmptyArray);
// Object structure validation
const hasRequiredFields = (obj) => {
return obj &&
typeof obj.name === 'string' &&
typeof obj.age === 'number' &&
obj.age > 0;
};
expect({name: 'John', age: 30}).to.satisfy(hasRequiredFields);
expect({name: 'John'}).to.not.satisfy(hasRequiredFields);const Code = require('@hapi/code');
const expect = Code.expect;
// Multiple validation approaches
const email = 'user@example.com';
expect(email)
.to.be.a.string()
.and.match(/^[^@]+@[^@]+\.[^@]+$/)
.and.satisfy(e => e.length > 5)
.and.include('@');
// Complex object validation
class Product {
constructor(name, price) {
this.name = name;
this.price = price;
}
}
const product = new Product('Laptop', 999);
expect(product)
.to.be.an.instanceof(Product)
.and.satisfy(p => p.price > 0)
.and.satisfy(p => typeof p.name === 'string');// Reusable validators
const validators = {
isValidUrl: (url) => {
try {
new URL(url);
return true;
} catch {
return false;
}
},
isPositiveInteger: (num) => {
return Number.isInteger(num) && num > 0;
},
isValidDate: (dateStr) => {
const date = new Date(dateStr);
return date instanceof Date && !isNaN(date);
},
hasMinLength: (minLen) => (str) => {
return typeof str === 'string' && str.length >= minLen;
}
};
// Usage
expect('https://example.com').to.satisfy(validators.isValidUrl);
expect(42).to.satisfy(validators.isPositiveInteger);
expect('2023-12-01').to.satisfy(validators.isValidDate);
expect('hello world').to.satisfy(validators.hasMinLength(5));// Common regex patterns
const patterns = {
email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
phone: /^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/,
url: /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)$/,
uuid: /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
ipv4: /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/,
creditCard: /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|3[0-9]{13}|6(?:011|5[0-9]{2})[0-9]{12})$/
};
// Validation usage
expect('user@domain.com').to.match(patterns.email);
expect('555-123-4567').to.match(patterns.phone);
expect('https://www.example.com').to.match(patterns.url);
expect('550e8400-e29b-41d4-a716-446655440000').to.match(patterns.uuid);// Graceful error handling in validators
const safeValidator = (value) => {
try {
// Some complex validation logic
return value && value.toString().length > 0;
} catch (err) {
return false; // Return false instead of throwing
}
};
expect('test').to.satisfy(safeValidator);
expect(null).to.not.satisfy(safeValidator);
// Type checking in validators
const typeAwareValidator = (value) => {
if (typeof value !== 'string') return false;
return value.startsWith('prefix_');
};
expect('prefix_test').to.satisfy(typeAwareValidator);
expect(123).to.not.satisfy(typeAwareValidator);
// Multiple condition validators
const complexValidator = (obj) => {
return obj &&
typeof obj === 'object' &&
Array.isArray(obj.items) &&
obj.items.length > 0 &&
obj.items.every(item => typeof item === 'string');
};
expect({items: ['a', 'b', 'c']}).to.satisfy(complexValidator);
expect({items: []}).to.not.satisfy(complexValidator);
expect({items: [1, 2, 3]}).to.not.satisfy(complexValidator);// Domain-specific validation
class UserAccount {
constructor(username, email, age) {
this.username = username;
this.email = email;
this.age = age;
}
}
const isValidUserAccount = (account) => {
return account instanceof UserAccount &&
typeof account.username === 'string' &&
account.username.length >= 3 &&
/^[^@]+@[^@]+\.[^@]+$/.test(account.email) &&
typeof account.age === 'number' &&
account.age >= 13;
};
const user = new UserAccount('john_doe', 'john@example.com', 25);
expect(user)
.to.be.an.instanceof(UserAccount)
.and.satisfy(isValidUserAccount);
// API response validation
const isValidApiResponse = (response) => {
return response &&
typeof response.status === 'number' &&
response.status >= 200 &&
response.status < 300 &&
response.data !== undefined;
};
const apiResponse = {status: 200, data: {id: 1, name: 'Test'}};
expect(apiResponse).to.satisfy(isValidApiResponse);