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
Behavior modification system using flags and grammar words for readable, flexible assertions.
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 readabilityUsage 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);Flags modify the behavior of assertions. Each flag toggles its state when accessed, allowing complex assertion logic.
Inverses the expected result of any assertion.
/**
* Inverses the expected result of the assertion
* Special behavior: For functions and promises, restricts available methods
*/
notUsage 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!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()
*/
onceUsage 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 twiceRequires 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()
*/
onlyUsage 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'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()
*/
partUsage 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 matchPerforms 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()
*/
shallowUsage 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 !== obj1Flags 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 thisFlags 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()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 twiceconst 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();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
}// 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