Expressive, readable, framework-agnostic BDD-style assertion library for JavaScript testing.
—
Methods for advanced pattern matching, validation, and testing using regular expressions, functions, and object matchers.
Test that a value matches a pattern using regular expressions, functions, or object matchers.
/**
* Assert that the value matches the specified pattern, function, or object
* @param pattern - RegExp, function, or object to match against
* @param description - Optional error message
* @returns This assertion for chaining
*/
match(pattern: RegExp | Function | object, description?: string): Assertion;Usage:
import should from 'should';
// String matching with RegExp
'hello123'.should.match(/^hello\d+$/);
'test@example.com'.should.match(/\w+@\w+\.\w+/);
// Function matching - return boolean
'positive123'.should.match(str => str.includes('positive'));
(42).should.match(num => num > 0);
// Function matching - throw for detailed assertion
const user = { name: 'John', age: 25 };
user.should.match(obj => {
obj.should.have.property('name').which.is.a.String();
obj.should.have.property('age').which.is.above(18);
});
// Object matching - matches properties recursively
const data = { a: 'foo', c: 'barfoo', d: 42 };
data.should.match({
a: /foo$/,
c: str => str.includes('bar'),
d: 42
});
// Array matching - matches by index
[10, 'abc', { d: 10 }].should.match({
'0': 10,
'1': /^ab/,
'2': obj => obj.should.have.property('d', 10)
});Test that every element in a collection matches a pattern.
/**
* Assert that every element matches the pattern
* @param pattern - RegExp, string, or function to match against
* @param description - Optional error message
* @returns This assertion for chaining
*/
matchEach(pattern: RegExp | string | Function, description?: string): Assertion;
/**
* Alias for matchEach - assert that every element matches the pattern
*/
matchEvery(pattern: RegExp | string | Function, description?: string): Assertion;Usage:
// Array - all elements match pattern
['hello', 'world', 'test'].should.matchEach(/\w+/);
[1, 2, 3, 4, 5].should.matchEach(num => num > 0);
// String matching
['apple', 'banana', 'cherry'].should.matchEach(str => str.length > 3);
// Array - all identical values
['a', 'a', 'a'].should.matchEach('a');
[42, 42, 42].should.matchEach(42);
// Object values - all match pattern
{ x: 'apple', y: 'banana', z: 'cherry' }.should.matchEach(/\w+/);
{ a: 10, b: 20, c: 30 }.should.matchEach(num => num > 5);
// Complex validation with functions
const words = ['hello', 'world', 'javascript'];
words.should.matchEach(word => {
word.should.be.a.String();
word.should.have.property('length').above(3);
});
const numbers = [1, 2, 3, 4, 5];
numbers.should.matchEach(n => {
n.should.be.a.Number();
n.should.be.above(0);
});Test that at least one element in a collection matches a pattern.
/**
* Assert that at least one element matches the pattern
* @param pattern - RegExp, string, or function to match against
* @param description - Optional error message
* @returns This assertion for chaining
*/
matchAny(pattern: RegExp | string | Function, description?: string): Assertion;
/**
* Alias for matchAny - assert that at least one element matches the pattern
*/
matchSome(pattern: RegExp | string | Function, description?: string): Assertion;Usage:
// Array - at least one matches
['hello', 'world', 'test'].should.matchAny(/^w/); // 'world' matches
[1, -2, 3].should.matchAny(num => num < 0); // -2 matches
// String matching
['apple', 'banana', 'cherry'].should.matchAny(str => str.startsWith('b')); // 'banana'
// Mixed array - some match, some don't
['abc', 123, 'def'].should.matchAny(/\w+/); // strings match
// Object values - at least one matches
{ x: 'apple', y: 'banana', z: 'cherry' }.should.matchAny(/^b/); // 'banana'
{ a: 'test', b: 'hello', c: 'world' }.should.matchAny('hello');
// Complex validation
const mixed = ['short', 'a', 'medium'];
mixed.should.matchAny(word => word.length > 5); // 'medium' matches
// Validation - at least one valid email
const emails = ['invalid', 'test@example.com', 'also-invalid'];
emails.should.matchAny(/\w+@\w+\.\w+/);// Email format validation
const email = 'user@example.com';
email.should.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);
// URL validation
const url = 'https://secure.example.com/path';
url.should.match(/^https?:\/\/[\w.-]+\/.*$/);
// Phone number formats
const phone = '(555) 123-4567';
phone.should.match(/^\(\d{3}\) \d{3}-\d{4}$/);// API response validation
const response = {
status: 'success',
data: { id: 123, name: 'John' },
timestamp: '2023-12-25T10:00:00Z'
};
response.should.match({
status: str => ['success', 'error'].includes(str),
data: obj => {
obj.should.have.property('id').which.is.a.Number();
obj.should.have.property('name').which.is.a.String();
},
timestamp: /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/
});// All user objects have required fields
const users = [
{ id: 1, name: 'Alice', email: 'alice@test.com' },
{ id: 2, name: 'Bob', email: 'bob@test.com' }
];
users.should.matchEach(user => {
user.should.have.property('id').which.is.a.Number();
user.should.have.property('name').which.is.a.String();
user.should.have.property('email').which.match(/@/);
});
// At least one admin user
const roles = ['user', 'admin', 'user', 'guest'];
roles.should.matchAny('admin');
// All scores are passing grades
const scores = [85, 92, 78, 88, 95];
scores.should.matchEach(score => score >= 70);// Validate error has expected properties and message pattern
const error = new Error('Network timeout after 5000ms');
error.should.match({
message: /timeout after \d+ms/,
name: 'Error'
});
// Custom error validation
function validateAPIError(err) {
err.should.match({
status: num => [400, 401, 403, 404, 500].includes(num),
message: str => str.length > 0,
details: obj => obj.should.be.an.Object()
});
}// Mixed data validation
const data = [42, 'hello', true, { key: 'value' }, [1, 2, 3]];
// At least one of each type
data.should.matchAny(item => typeof item === 'number');
data.should.matchAny(item => typeof item === 'string');
data.should.matchAny(item => typeof item === 'boolean');
data.should.matchAny(item => Array.isArray(item));
data.should.matchAny(item => typeof item === 'object' && !Array.isArray(item));
// All elements are truthy
const values = [1, 'test', true, {}, []];
values.should.matchEach(val => !!val);// Good: Specific regex for known format
'2023-12-25'.should.match(/^\d{4}-\d{2}-\d{2}$/);
// Good: Function for complex logic
user.should.match(u => {
u.should.have.property('age').which.is.above(18);
return u.role === 'admin' || u.permissions.includes('write');
});
// Good: Object matching for structure validation
response.should.match({
data: arr => arr.should.be.an.Array(),
pagination: obj => {
obj.should.have.properties('page', 'limit', 'total');
}
});// Ensure type before pattern matching
const value = '123-45-6789';
value.should.be.a.String().and.match(/^\d{3}-\d{2}-\d{4}$/);
// Collection type before element matching
const items = ['a', 'b', 'c'];
items.should.be.an.Array().and.matchEach(/^[a-z]$/);// Descriptive error messages
const password = 'weak';
password.should.match(
/^(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*])[A-Za-z\d!@#$%^&*]{8,}$/,
'Password must contain uppercase, digit, special char, min 8 chars'
);
// Validate before matching
function validateConfig(config) {
config.should.be.an.Object('Config must be an object');
config.should.match({
env: str => ['dev', 'staging', 'prod'].includes(str),
port: num => num > 0 && num < 65536,
database: obj => obj.should.have.properties('host', 'port', 'name')
});
}Install with Tessl CLI
npx tessl i tessl/npm-should