Core functionality for creating test scripts and organizing tests into experiments with setup/teardown hooks. This provides the fundamental building blocks for structuring test suites.
Creates a test script instance that provides the foundation for organizing and executing tests.
/**
* Creates a test script with test organization capabilities
* @param options - Optional configuration for script behavior
* @returns Script instance with test organization methods
*/
function script(options?: ScriptOptions): Script;
interface ScriptOptions {
/** Determines if execution should be delayed until CLI runs explicitly (default: true) */
schedule?: boolean;
/** CLI configuration options */
cli?: CliOptions;
}
interface Script {
experiment: Experiment;
describe: Experiment; // Alias for experiment
suite: Experiment; // Alias for experiment
test: Test;
it: Test; // Alias for test
before: Setup;
after: Setup;
beforeEach: Setup;
afterEach: Setup;
}Usage Examples:
const Lab = require('@hapi/lab');
// Basic script creation
const lab = exports.lab = Lab.script();
// Script with options
const lab = exports.lab = Lab.script({
schedule: false // Don't auto-execute
});Creates test experiments (suites) for organizing related tests with shared setup and teardown.
/**
* Creates a test experiment for organizing related tests
* @param title - Descriptive name for the experiment
* @param content - Function containing tests and sub-experiments
*/
experiment(title: string, content: () => void): void;
/**
* Creates a test experiment with options
* @param title - Descriptive name for the experiment
* @param options - Experiment configuration (excluding plan)
* @param content - Function containing tests and sub-experiments
*/
experiment(title: string, options: Omit<TestOptions, 'plan'>, content: () => void): void;
interface Experiment {
(title: string, content: () => void): void;
(title: string, options: Omit<TestOptions, 'plan'>, content: () => void): void;
/** Run only this experiment, skipping all others */
only(title: string, content: () => void): void;
only(title: string, options: Omit<TestOptions, 'plan'>, content: () => void): void;
/** Skip this experiment during execution */
skip(title: string, content: () => void): void;
skip(title: string, options: Omit<TestOptions, 'plan'>, content: () => void): void;
}Usage Examples:
// Basic experiment
experiment('User authentication', () => {
// tests go here
});
// Experiment with timeout
experiment('Database operations', { timeout: 5000 }, () => {
// longer-running tests
});
// Skip experiment
experiment.skip('Broken feature', () => {
// temporarily disabled tests
});
// Run only this experiment
experiment.only('Debug this', () => {
// focus on specific tests
});Defines individual test cases within experiments.
/**
* Defines an individual test case
* @param title - Descriptive name for the test
* @param testFn - Test function to execute
*/
test(title: string, testFn: Action): void;
/**
* Defines a test case with options
* @param title - Descriptive name for the test
* @param options - Test configuration options
* @param testFn - Test function to execute
*/
test(title: string, options: TestOptions, testFn: Action): void;
interface Test {
(title: string, test: Action): void;
(title: string, options: TestOptions, test: Action): void;
/** Run only this test, skipping all others */
only(title: string, test: Action): void;
only(title: string, options: TestOptions, test: Action): void;
/** Skip this test during execution */
skip(title: string, test: Action): void;
skip(title: string, options: TestOptions, test: Action): void;
}
interface Action {
/** Test function receiving flags for utilities and context */
(flags: Flags): Promise<any> | void;
}Usage Examples:
// Synchronous test
test('validates user input', () => {
expect(validateEmail('test@example.com')).to.be.true();
});
// Asynchronous test
test('fetches user data', async () => {
const user = await getUserById(123);
expect(user.name).to.equal('John Doe');
});
// Test with options
test('complex operation', { timeout: 10000, plan: 3 }, (flags) => {
// Test expecting exactly 3 assertions with 10s timeout
});
// Using test flags
test('cleanup test', (flags) => {
flags.onCleanup = () => cleanupDatabase();
// test implementation
});Provides setup and teardown functionality for experiments with flexible timing options.
/**
* Runs setup function before all tests in the current experiment
* @param action - Setup function to execute
*/
before(action: Action): void;
/**
* Runs setup function with options before all tests
* @param options - Setup configuration
* @param action - Setup function to execute
*/
before(options: TestOptions, action: Action): void;
/**
* Runs teardown function after all tests in the current experiment
* @param action - Teardown function to execute
*/
after(action: Action): void;
/**
* Runs teardown function with options after all tests
* @param options - Teardown configuration
* @param action - Teardown function to execute
*/
after(options: TestOptions, action: Action): void;
/**
* Runs setup function before each test in the current experiment
* @param action - Setup function to execute
*/
beforeEach(action: Action): void;
/**
* Runs setup function with options before each test
* @param options - Setup configuration
* @param action - Setup function to execute
*/
beforeEach(options: TestOptions, action: Action): void;
/**
* Runs teardown function after each test in the current experiment
* @param action - Teardown function to execute
*/
afterEach(action: Action): void;
/**
* Runs teardown function with options after each test
* @param options - Teardown configuration
* @param action - Teardown function to execute
*/
afterEach(options: TestOptions, action: Action): void;
interface Setup {
(action: Action): void;
(options: TestOptions, action: Action): void;
}Usage Examples:
experiment('Database tests', () => {
before(async () => {
await setupDatabase();
});
after(async () => {
await cleanupDatabase();
});
beforeEach(() => {
resetTestData();
});
afterEach(() => {
clearCache();
});
// Setup with timeout
before({ timeout: 30000 }, async () => {
await longRunningSetup();
});
test('user creation', () => {
// test implementation
});
});The Flags interface provides utilities and context available to test functions and setup/teardown hooks.
interface Flags {
/** Shared object for passing data between setup, tests, and teardown */
readonly context: Record<string, any>;
/**
* Wraps a function to require a specific number of invocations
* @param func - Function to wrap
* @param count - Required number of calls
* @returns Wrapped function that tracks call count
*/
mustCall<T extends (...args: any[]) => any>(func: T, count: number): T;
/**
* Adds notes to the test log for console reporter output
* @param note - String to include in console output
*/
note(note: string): void;
/** Cleanup function executed after test completion */
onCleanup?: Action;
/** Override for global exception handling */
onUncaughtException?: ErrorHandler;
/** Override for global rejection handling */
onUnhandledRejection?: ErrorHandler;
}Usage Examples:
test('callback verification', (flags) => {
const callback = flags.mustCall((result) => {
expect(result).to.equal('success');
}, 2); // Must be called exactly twice
asyncFunction(callback);
anotherAsyncFunction(callback);
});
test('with context', (flags) => {
flags.context.userId = 123;
flags.note('Testing with user ID: 123');
flags.onCleanup = () => {
cleanupUser(flags.context.userId);
};
});Configuration options available for tests, experiments, and setup/teardown hooks.
interface TestOptions {
/** Skip this test/experiment during execution */
skip?: boolean;
/** Run only this test/experiment, skipping all others */
only?: boolean;
/** Override default timeout in milliseconds (default: 2000) */
timeout?: number;
/** Expected number of assertions this test must execute */
plan?: number;
/** Retry failed tests (true for default retries, number for specific count) */
retry?: number | boolean;
}