Comprehensive test utility framework for Node.js applications with advanced testing capabilities
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
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;
}