or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

assertion-modifiers.mdcontent-testing.mdcore-functions.mdfunction-testing.mdindex.mdnumeric-comparisons.mdpattern-validation.mdpromise-testing.mdsettings.mdstring-assertions.mdtype-assertions.mdvalue-assertions.md
tile.json

assertion-modifiers.mddocs/

Assertion Modifiers

Behavior modification system using flags and grammar words for readable, flexible assertions.

Grammar Words

Grammar words have no functional impact but improve assertion readability by making them more natural language-like.

// Grammar properties (all return 'this' for chaining)
a       // Article for readability
an      // Article for readability  
and     // Conjunction for chaining assertions
at      // Preposition for readability
be      // Verb for readability
have    // Verb for readability
in      // Preposition for readability
to      // Infinitive marker for readability

Usage Examples:

const Code = require('@hapi/code');
const expect = Code.expect;

// Natural language-like assertions
expect(10).to.be.above(5);
expect('hello').to.be.a.string();
expect([1, 2]).to.be.an.array();
expect(20).to.be.at.least(20);
expect('abc').to.have.length(3);
expect(6).to.be.in.range(5, 10);

// Chaining with 'and'
expect('hello world')
    .to.be.a.string()
    .and.contain('world')
    .and.have.length(11);

Behavior Flags

Flags modify the behavior of assertions. Each flag toggles its state when accessed, allowing complex assertion logic.

not Flag

Inverses the expected result of any assertion.

/**
 * Inverses the expected result of the assertion
 * Special behavior: For functions and promises, restricts available methods
 */
not

Usage Examples:

// Basic negation
expect(10).to.not.be.above(20);
expect('hello').to.not.be.a.number();
expect([]).to.not.have.length(5);

// Chaining with negation
expect(user)
    .to.be.an.object()
    .and.not.be.empty()
    .and.not.be.null();

// Special behavior with functions (cannot specify error type/message)
expect(nonThrowingFunction).to.not.throw();
// expect(nonThrowingFunction).to.not.throw(Error); // This would error!

// Special behavior with promises
await expect(resolvingPromise).to.not.reject();
// await expect(resolvingPromise).to.not.reject(Error); // This would error!

once Flag

Requires that inclusion matches appear only once in the provided value. Used with

include()
methods.

/**
 * Requires inclusion matches appear only once
 * Used with: include(), includes(), contain(), contains()
 */
once

Usage Examples:

// String occurrence checking
expect('hello world').to.once.include('o'); // Fails - 'o' appears twice
expect('hello world').to.once.include('h'); // Passes - 'h' appears once

// Array element checking  
expect([1, 2, 3]).to.once.include(2); // Passes - 2 appears once
expect([1, 2, 2, 3]).to.once.include(2); // Fails - 2 appears twice

// Multiple elements
expect('abcde').to.once.include(['a', 'e']); // Passes - each appears once
expect('abccde').to.once.include(['a', 'c']); // Fails - 'c' appears twice

only Flag

Requires that only the provided elements appear in the provided value (exact match with no extra elements). Used with

include()
methods.

/**
 * Requires exact match with no extra elements
 * Used with: include(), includes(), contain(), contains()
 */
only

Usage Examples:

// Array exact matching
expect([1, 2, 3]).to.only.include([1, 2, 3]); // Passes - exact match
expect([1, 2, 3]).to.only.include([1, 2]); // Fails - array has extra element (3)
expect([1, 2]).to.only.include([1, 2, 3]); // Fails - expected element (3) not present

// Order doesn't matter
expect([3, 1, 2]).to.only.include([1, 2, 3]); // Passes - same elements

// Object key matching
expect({a: 1, b: 2}).to.only.include(['a', 'b']); // Passes - exact keys
expect({a: 1, b: 2, c: 3}).to.only.include(['a', 'b']); // Fails - extra key 'c'

// String character matching
expect('abc').to.only.include(['a', 'b', 'c']); // Passes - exact characters
expect('abcd').to.only.include(['a', 'b', 'c']); // Fails - extra character 'd'

part Flag

Allows partial matching when asserting inclusion instead of full comparison. Used with

include()
methods.

/**
 * Allows partial matching instead of full comparison
 * Used with: include(), includes(), contain(), contains()
 */
part

Usage Examples:

// Object partial matching
expect({a: 1, b: 2, c: 3}).to.part.include({a: 1}); // Passes - partial match
expect({a: 1, b: 2}).to.part.include({a: 1, c: 3}); // Fails - 'c' not present

// Array of objects partial matching
expect([{a: 1, b: 2}, {c: 3, d: 4}]).to.part.include([{a: 1}]); // Passes
expect([{a: 1, b: 2}]).to.part.include([{a: 1, c: 3}]); // Fails - 'c' not in object

// Nested object partial matching
const users = [
    {id: 1, name: 'Alice', profile: {age: 25, city: 'NYC'}},
    {id: 2, name: 'Bob', profile: {age: 30, city: 'LA'}}
];

expect(users).to.part.include([{id: 1, name: 'Alice'}]); // Passes - partial match
expect(users).to.part.include([{profile: {age: 25}}]); // Passes - nested partial match

shallow Flag

Performs comparison using strict equality (

===
) instead of deep comparison. Used with
equal()
and
include()
methods.

/**
 * Use strict equality (===) instead of deep comparison
 * Used with: equal(), equals(), include(), includes(), contain(), contains()
 */
shallow

Usage Examples:

// Object reference comparison
const obj1 = {a: 1, b: 2};
const obj2 = {a: 1, b: 2};  // Different object, same content
const obj3 = obj1;          // Same reference

// Deep comparison (default)
expect(obj1).to.equal(obj2); // Passes - deep equality
expect(obj1).to.equal(obj3); // Passes - same reference

// Shallow comparison
expect(obj1).to.shallow.equal(obj3); // Passes - same reference
expect(obj1).to.not.shallow.equal(obj2); // Passes - different references

// Array reference comparison
const arr1 = [1, 2, 3];
const arr2 = [1, 2, 3];
const arr3 = arr1;

expect(arr1).to.equal(arr2); // Passes - deep equality
expect(arr1).to.shallow.equal(arr3); // Passes - same reference
expect(arr1).to.not.shallow.equal(arr2); // Passes - different references

// Shallow inclusion
expect([obj1, obj2]).to.shallow.include(obj1); // Passes - same reference
expect([obj1, obj2]).to.not.shallow.include(obj3); // Passes if obj3 !== obj1

Flag Behavior and Interactions

Flag Toggle Behavior

Flags toggle their state each time they're accessed:

// Flag toggling
expect(10).to.not.not.be.above(5); // Double negation - 'not' cancels out
expect('hello').to.shallow.shallow.equal('hello'); // Double shallow - cancels out

// This can be confusing, so avoid multiple flag usage
expect(value).to.not.be.above(10); // Clear and readable
// expect(value).to.not.not.not.be.above(10); // Confusing - avoid this

Flag Scope and Reset

Flags apply to the next assertion method and then reset:

// Flags reset after each assertion
expect(obj)
    .to.be.an.object()        // No flags active
    .and.not.be.empty()       // 'not' flag active for empty() only
    .and.have.length.above(0); // No flags active for above()

// Each assertion gets its own flag state
expect(arr)
    .to.shallow.include(item1)  // 'shallow' applies to this include()
    .and.include(item2);        // No 'shallow' flag for this include()

Combining Multiple Flags

Multiple flags can be combined in a single assertion:

// Multiple flags on one assertion
expect([{a: 1, b: 2}, {c: 3, d: 4}])
    .to.once.part.include([{a: 1}]); // Both 'once' and 'part' flags active

expect([obj1, obj1, obj2])
    .to.not.once.shallow.include(obj1); // 'not', 'once', and 'shallow' flags

// String with multiple flags
expect('hello world hello')
    .to.not.once.include('hello'); // Fails because 'hello' appears twice

Advanced Usage Patterns

Complex Assertion Chains

const Code = require('@hapi/code');
const expect = Code.expect;

// Complex object validation
const user = {
    id: 123,
    name: 'John Doe',
    email: 'john@example.com',
    roles: ['user', 'admin'],
    settings: {
        theme: 'dark',
        notifications: true
    }
};

expect(user)
    .to.be.an.object()
    .and.not.be.empty()
    .and.include(['id', 'name', 'email'])
    .and.not.include(['password', 'secret']);

expect(user.roles)
    .to.be.an.array()
    .and.only.include(['user', 'admin'])
    .and.once.include('admin');

expect(user.settings)
    .to.part.include({theme: 'dark'})
    .and.not.be.empty();

Flag-Specific Error Messages

Understanding how flags affect error messages:

// Different error messages based on flags
try {
    expect([1, 2, 3]).to.only.include([1, 2]); // Fails
} catch (err) {
    console.log(err.message); // Includes 'only' context
}

try {
    expect('hello hello').to.once.include('hello'); // Fails  
} catch (err) {
    console.log(err.message); // Includes 'once' context
}

try {
    expect(obj1).to.shallow.equal(obj2); // Fails if different references
} catch (err) {
    console.log(err.message); // Shows reference comparison failure
}

Performance Considerations

// Shallow comparisons are faster for large objects
const largeObj1 = generateLargeObject();
const largeObj2 = generateLargeObject();
const largeObj3 = largeObj1;

// Fast reference check
expect(largeObj1).to.shallow.equal(largeObj3); // Quick reference comparison

// Slower deep comparison
expect(largeObj1).to.equal(largeObj2); // Deep traversal of object properties