CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-should

Expressive, readable, framework-agnostic BDD-style assertion library for JavaScript testing.

Pending
Overview
Eval results
Files

pattern-matching.mddocs/

Pattern Matching

Methods for advanced pattern matching, validation, and testing using regular expressions, functions, and object matchers.

Core Pattern Matching

match()

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)
});

Collection Pattern Matching

matchEach() / matchEvery()

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);
});

matchAny() / matchSome()

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+/);

Advanced Pattern Matching Examples

String Validation

// 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}$/);

Object Structure Validation

// 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$/
});

Collection Validation

// 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);

Error Message Validation

// 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()
  });
}

Data Type Validation

// 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);

Pattern Matching Best Practices

Use Appropriate Patterns

// 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');
  }
});

Combine with Type Assertions

// 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]$/);

Error Handling

// 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

docs

basic-assertions.md

configuration.md

containment-assertions.md

error-assertions.md

index.md

number-assertions.md

pattern-matching.md

promise-assertions.md

property-assertions.md

string-assertions.md

type-assertions.md

tile.json