Extensible architecture for adding custom assertion methods, properties, and functionality to all assertion styles. Chai's plugin system allows developers to create reusable assertion extensions that integrate seamlessly with the existing API.
Registers a plugin function that extends Chai's functionality across all assertion interfaces.
/**
* Registers a plugin that extends Chai functionality
* @param plugin - Plugin function that receives chai and utils parameters
* @returns ChaiStatic instance for chaining
*/
function use(plugin: (chai: ChaiStatic, utils: ChaiUtils) => void): ChaiStatic;Plugin functions receive the chai instance and utilities for extending functionality.
interface PluginFunction {
/**
* @param chai - The main Chai export object
* @param utils - Chai utility functions for plugin development
*/
(chai: ChaiStatic, utils: ChaiUtils): void;
}
interface ChaiStatic {
expect: ExpectStatic;
assert: AssertStatic;
should(): Should;
Should: ShouldStatic;
use: (plugin: PluginFunction) => ChaiStatic;
util: ChaiUtils;
config: Config;
AssertionError: typeof AssertionError;
Assertion: typeof Assertion;
}Add chainable properties to the Assertion prototype.
import { use } from "chai";
// Plugin that adds custom properties
use(function(chai, utils) {
const { Assertion } = chai;
// Add a simple property
Assertion.addProperty('positive', function() {
const obj = utils.flag(this, 'object');
this.assert(
obj > 0,
'expected #{this} to be positive',
'expected #{this} to not be positive'
);
});
// Add property with modifier behavior
Assertion.addProperty('api', function() {
utils.flag(this, 'api', true);
});
});
// Usage
expect(5).to.be.positive;
expect(-1).to.not.be.positive;Add chainable methods that accept parameters.
use(function(chai, utils) {
const { Assertion } = chai;
// Add method with parameters
Assertion.addMethod('divisibleBy', function(divisor) {
const obj = utils.flag(this, 'object');
this.assert(
obj % divisor === 0,
'expected #{this} to be divisible by #{exp}',
'expected #{this} to not be divisible by #{exp}',
divisor
);
});
// Add method with complex logic
Assertion.addMethod('between', function(min, max) {
const obj = utils.flag(this, 'object');
this.assert(
obj >= min && obj <= max,
'expected #{this} to be between #{exp} and #{act}',
'expected #{this} to not be between #{exp} and #{act}',
min,
max
);
});
});
// Usage
expect(10).to.be.divisibleBy(5);
expect(7).to.be.between(5, 10);Add methods that can be chained and also act as properties.
use(function(chai, utils) {
const { Assertion } = chai;
// Chainable method with both method and property behavior
Assertion.addChainableMethod(
'length',
function(expectedLength) {
// Method behavior: expect(array).to.have.length(5)
const obj = utils.flag(this, 'object');
this.assert(
obj.length === expectedLength,
'expected #{this} to have length #{exp}',
'expected #{this} to not have length #{exp}',
expectedLength
);
},
function() {
// Property behavior: expect(array).to.have.length.above(3)
const obj = utils.flag(this, 'object');
utils.flag(this, 'object', obj.length);
}
);
});
// Usage
expect([1, 2, 3]).to.have.length(3); // Method usage
expect([1, 2, 3]).to.have.length.above(2); // Property usageAdd functions to the assert interface for TDD-style testing.
use(function(chai, utils) {
const { assert } = chai;
// Add assert-style function
assert.isPositive = function(value, message) {
assert(
value > 0,
message || `expected ${value} to be positive`
);
};
assert.isDivisibleBy = function(value, divisor, message) {
assert(
value % divisor === 0,
message || `expected ${value} to be divisible by ${divisor}`
);
};
});
// Usage
assert.isPositive(5);
assert.isDivisibleBy(10, 2);Add functionality to the should interface.
use(function(chai, utils) {
const should = chai.should();
// Add should-style static methods
should.bePositive = function(value, message) {
new chai.Assertion(value, message).to.be.positive;
};
// Methods are automatically available on objects after should() is called
});
// Usage (after calling should())
should.bePositive(5);
(5).should.be.positive;Chai provides utility functions for plugin development.
interface ChaiUtils {
/**
* Get or set flags on assertion instance
* @param assertion - Assertion instance
* @param key - Flag key
* @param value - Flag value (optional, for setting)
*/
flag(assertion: Assertion, key: string, value?: any): any;
/**
* Execute an assertion with proper error handling
* @param assertion - Assertion instance
* @param expression - Boolean expression to test
*/
test(assertion: Assertion, expression: boolean): void;
/**
* Get the type of a value as a string
* @param obj - Value to get type of
*/
type(obj: any): string;
/**
* Generate error message for assertion
* @param assertion - Assertion instance
* @param args - Message template arguments
*/
getMessage(assertion: Assertion, args: any[]): string;
/**
* Get the actual value being asserted against
* @param assertion - Assertion instance
* @param args - Additional arguments
*/
getActual(assertion: Assertion, args: any[]): any;
/**
* Inspect/stringify a value for display
* @param obj - Value to inspect
* @param showHidden - Show hidden properties
* @param depth - Inspection depth
* @param colors - Use colors in output
*/
inspect(obj: any, showHidden?: boolean, depth?: number, colors?: boolean): string;
/**
* Deep equality comparison
* @param a - First value
* @param b - Second value
*/
eql(a: any, b: any): boolean;
/**
* Transfer flags from one assertion to another
* @param assertion - Source assertion
* @param target - Target assertion
* @param includeAll - Include all flags or just specific ones
*/
transferFlags(assertion: Assertion, target: Assertion, includeAll?: boolean): void;
}use(function(chai, utils) {
const { Assertion } = chai;
Assertion.addMethod('jsonSchema', function(schema) {
const obj = utils.flag(this, 'object');
// Simple validation (real plugin would use ajv or similar)
const isValid = validateJsonSchema(obj, schema);
const errors = getValidationErrors(obj, schema);
this.assert(
isValid,
'expected #{this} to match JSON schema',
'expected #{this} to not match JSON schema'
);
});
function validateJsonSchema(obj, schema) {
// Implementation details...
return true; // Simplified
}
function getValidationErrors(obj, schema) {
// Implementation details...
return [];
}
});
// Usage
expect(userData).to.match.jsonSchema(userSchema);use(function(chai, utils) {
const { Assertion } = chai;
// Status code assertion
Assertion.addMethod('status', function(expectedStatus) {
const response = utils.flag(this, 'object');
this.assert(
response.status === expectedStatus,
'expected response to have status #{exp} but got #{act}',
'expected response to not have status #{exp}',
expectedStatus,
response.status
);
});
// Header assertion
Assertion.addMethod('header', function(headerName, expectedValue) {
const response = utils.flag(this, 'object');
const headerValue = response.headers[headerName.toLowerCase()];
if (arguments.length === 1) {
// Check header existence
this.assert(
headerValue !== undefined,
'expected response to have header #{exp}',
'expected response to not have header #{exp}',
headerName
);
} else {
// Check header value
this.assert(
headerValue === expectedValue,
'expected header #{exp} to have value #{act}',
'expected header #{exp} to not have value #{act}',
headerName,
expectedValue
);
}
});
});
// Usage
expect(response).to.have.status(200);
expect(response).to.have.header('content-type');
expect(response).to.have.header('content-type', 'application/json');use(function(chai, utils) {
const { Assertion } = chai;
// Element existence
Assertion.addMethod('element', function(selector) {
const context = utils.flag(this, 'object') || document;
const element = context.querySelector(selector);
this.assert(
element !== null,
'expected to find element #{exp}',
'expected to not find element #{exp}',
selector
);
utils.flag(this, 'object', element);
});
// Text content
Assertion.addMethod('text', function(expectedText) {
const element = utils.flag(this, 'object');
const actualText = element.textContent.trim();
this.assert(
actualText === expectedText,
'expected element to have text #{exp} but got #{act}',
'expected element to not have text #{exp}',
expectedText,
actualText
);
});
// CSS class
Assertion.addMethod('class', function(className) {
const element = utils.flag(this, 'object');
this.assert(
element.classList.contains(className),
'expected element to have class #{exp}',
'expected element to not have class #{exp}',
className
);
});
});
// Usage
expect(document).to.have.element('#my-button');
expect('#my-button').to.have.text('Click me');
expect('#my-button').to.have.class('btn-primary');// my-chai-plugin.js
export default function(chai, utils) {
// Plugin implementation
}
// Usage
import chai, { expect } from 'chai';
import myPlugin from 'my-chai-plugin';
chai.use(myPlugin);// types.d.ts
declare namespace Chai {
interface Assertion {
positive: Assertion;
divisibleBy(divisor: number): Assertion;
between(min: number, max: number): Assertion;
}
}
// plugin.ts
export default function(chai: any, utils: any): void {
// Implementation with TypeScript types
}