BDD assertion library designed to work seamlessly with the hapi ecosystem
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
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);