### Lab Experiment with Setup Hooks Source: https://github.com/hapijs/lab/blob/master/API.md Shows how to use `before()` and `beforeEach()` hooks within a Lab experiment for setup operations. Hooks can return Promises. ```javascript lab.experiment('math', () => { lab.before(() => { return new Promise((resolve) => { // Wait 1 second setTimeout(() => { resolve(); }, 1000); }); }); lab.beforeEach(() => { // Run before every single test }); lab.test('returns true when 1 + 1 equals 2', () => { expect(1 + 1).to.equal(2); }); }); ``` -------------------------------- ### Create a Test Script with Basic and Async Tests Source: https://context7.com/hapijs/lab/llms.txt Initializes a test script using `Lab.script()`. Includes examples of both synchronous and asynchronous tests using `@hapi/code` assertions. ```javascript const Code = require('@hapi/code'); const Lab = require('@hapi/lab'); const { expect } = Code; const lab = exports.lab = Lab.script(); // Basic sync test lab.test('adds two numbers correctly', () => { expect(1 + 1).to.equal(2); }); // Async test lab.test('resolves a promise', async () => { const result = await Promise.resolve('hello'); expect(result).to.equal('hello'); }); ``` -------------------------------- ### script.before() Source: https://github.com/hapijs/lab/blob/master/API.md Executes the provided action before the current experiment block is started. ```APIDOC ## script.before() ### Description Executes the provided action before the current experiment block is started. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - `options` - optional flags as describe in [`script.test()`](#scripttesttitle-options-test). - `action` - a sync or async function using the signature `function(flags)` where `flags` see [Flags](#flags). ``` -------------------------------- ### Basic Test Structure in Lab Source: https://github.com/hapijs/lab/blob/master/API.md Standard setup for a test file using Lab and Code. Requires @hapi/lab and @hapi/code. Exports the test script interface. ```javascript const Code = require('@hapi/code'); const Lab = require('@hapi/lab'); const { expect } = Code; const { it } = exports.lab = Lab.script(); it('returns true when 1 + 1 equals 2', () => { expect(1 + 1).to.equal(2); }); ``` -------------------------------- ### Install c8 for ESM Coverage Source: https://github.com/hapijs/lab/blob/master/API.md Install the c8 package as a development dependency to enable code coverage for ES modules with Lab. ```bash npm install --save-dev c8 ``` -------------------------------- ### Load Custom Reporters from Filesystem Source: https://github.com/hapijs/lab/blob/master/API.md Custom reporters can be loaded from the filesystem. If the name starts with './', it's relative to the current directory. Otherwise, it's loaded from `node_modules`. ```javascript '.'/custom-reporter' ``` ```javascript 'custom-reporter' ``` -------------------------------- ### Add Lab as Dev Dependency and Configure Test Scripts Source: https://github.com/hapijs/lab/blob/master/API.md Install @hapi/lab as a dev dependency and define 'test' and 'test-cov-html' scripts in package.json. The 'test' script enforces 100% code coverage. ```json { "devDependencies": { "@hapi/lab": "21.x.x" }, "scripts": { "test": "lab -t 100", "test-cov-html": "lab -r html -o coverage.html" } } ``` -------------------------------- ### BDD style tests with describe/it Source: https://context7.com/hapijs/lab/llms.txt Lab provides describe/it as aliases for experiment/test, enabling familiar BDD syntax without extra configuration. Use before/after for setup and teardown. ```javascript const Code = require('@hapi/code'); const Lab = require('@hapi/lab'); const { expect } = Code; // BDD style const { describe, it, before, after } = exports.lab = Lab.script(); describe('UserService', () => { before(() => { /* setup */ }); after(() => { /* teardown */ }); it('creates a user with a hashed password', async () => { const user = await UserService.create({ email: 'a@b.com', password: 'secret' }); expect(user.password).to.not.equal('secret'); expect(user.email).to.equal('a@b.com'); }); }); ``` -------------------------------- ### Lab Test with Promise Return Source: https://github.com/hapijs/lab/blob/master/API.md Illustrates how Lab tests and experiments can handle asynchronous operations by returning Promises. This example shows a test returning a promise. ```javascript lab.experiment('math', () => { lab.before(() => { return aFunctionReturningAPromise(); }); lab.test('returns true when 1 + 1 equals 2', () => { return aFunctionReturningAPromise() .then((aValue) => { expect(aValue).to.equal(expectedValue); }); }); }); ``` -------------------------------- ### Detect Missing Assertions with Lab and @hapi/code Source: https://github.com/hapijs/lab/blob/master/API.md When using @hapi/code, Lab can identify tests with missing or incomplete assertions. This example shows a test that passes but is marked as having an incomplete expectation. ```javascript describe('expectation', () => { it('Test should pass but get marked as having a missing expectation', () => { // Invalid and missing assertion - false is a method, not a property! // This test will pass. Lab.expect(true).to.be.false; }); }); ``` -------------------------------- ### script.suite(title, [options], content) Source: https://github.com/hapijs/lab/blob/master/API.md Sets up a test suite, identical to script.experiment(). ```APIDOC ## script.suite(title, [options], content) ### Description Same as [`script.experiment()`](#scriptexperimenttitle-options-content). ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - `title` - the experiment description. - `options` - optional settings: - `skip` - if `true`, sets the entire experiment content to be skipped during execution. Defaults to `false`. - `only` - if `true`, sets all other experiments to `skip`. Default to `false`. - `timeout` - overrides the default test timeout for tests and other timed operations. Defaults to `2000`. - `content` - a function with signature `function()` which can setup other experiments or tests. ``` -------------------------------- ### script.describe(title, [options], content) Source: https://github.com/hapijs/lab/blob/master/API.md Sets up an experiment (a group of tests), identical to script.experiment(). ```APIDOC ## script.describe(title, [options], content) ### Description Same as [`script.experiment()`](#scriptexperimenttitle-options-content). ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - `title` - the experiment description. - `options` - optional settings: - `skip` - if `true`, sets the entire experiment content to be skipped during execution. Defaults to `false`. - `only` - if `true`, sets all other experiments to `skip`. Default to `false`. - `timeout` - overrides the default test timeout for tests and other timed operations. Defaults to `2000`. - `content` - a function with signature `function()` which can setup other experiments or tests. ``` -------------------------------- ### script.experiment(title, [options], content) Source: https://github.com/hapijs/lab/blob/master/API.md Sets up an experiment, which is a group of tests. It accepts a title, optional settings, and a content function. ```APIDOC ## script.experiment(title, [options], content) ### Description Sets up an experiment (a group of tests). ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - `title` - the experiment description. - `options` - optional settings: - `skip` - if `true`, sets the entire experiment content to be skipped during execution. Defaults to `false`. - `only` - if `true`, sets all other experiments to `skip`. Default to `false`. - `timeout` - overrides the default test timeout for tests and other timed operations. Defaults to `2000`. - `content` - a function with signature `function()` which can setup other experiments or tests. ``` -------------------------------- ### Experiment lifecycle hooks with script.before and script.after Source: https://context7.com/hapijs/lab/llms.txt Run setup/teardown once before or after all tests in the current experiment block. Receive a `flags` object including `context` for sharing state. ```javascript const Code = require('@hapi/code'); const Lab = require('@hapi/lab'); const { expect } = Code; const lab = exports.lab = Lab.script(); let db; lab.experiment('database operations', () => { lab.before(async ({ context }) => { db = await connectToDatabase({ host: 'localhost', port: 5432 }); context.userId = await db.insertUser({ name: 'Alice' }); }); lab.after(async () => { await db.close(); }); lab.test('retrieves a user by id', async ({ context }) => { const user = await db.getUser(context.userId); expect(user.name).to.equal('Alice'); }); lab.test('updates a user name', async ({ context }) => { await db.updateUser(context.userId, { name: 'Bob' }); const user = await db.getUser(context.userId); expect(user.name).to.equal('Bob'); }); }); ``` -------------------------------- ### script.experiment.skip(title, [options], content) Source: https://github.com/hapijs/lab/blob/master/API.md Sets up an experiment that will be skipped during execution. ```APIDOC ## script.experiment.skip(title, [options], content) ### Description Same as [`script.experiment()`](#scriptexperimenttitle-options-content) with the `skip` option set to `true`. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - `title` - the experiment description. - `options` - optional settings: - `skip` - if `true`, sets the entire experiment content to be skipped during execution. Defaults to `false`. - `only` - if `true`, sets all other experiments to `skip`. Default to `false`. - `timeout` - overrides the default test timeout for tests and other timed operations. Defaults to `2000`. - `content` - a function with signature `function()` which can setup other experiments or tests. ``` -------------------------------- ### script.experiment.only(title, [options], content) Source: https://github.com/hapijs/lab/blob/master/API.md Sets up an experiment that will be the only one executed, skipping all others. ```APIDOC ## script.experiment.only(title, [options], content) ### Description Same as [`script.experiment()`](#scriptexperimenttitle-options-content) with the `only` option set to `true`. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - `title` - the experiment description. - `options` - optional settings: - `skip` - if `true`, sets the entire experiment content to be skipped during execution. Defaults to `false`. - `only` - if `true`, sets all other experiments to `skip`. Default to `false`. - `timeout` - overrides the default test timeout for tests and other timed operations. Defaults to `2000`. - `content` - a function with signature `function()` which can setup other experiments or tests. ``` -------------------------------- ### Behavior Driven Development (BDD) Style in Lab Source: https://github.com/hapijs/lab/blob/master/API.md Sets up Lab to follow a Behavior Driven Development (BDD) style using `describe`, `before`, `after`, and `it` functions. ```javascript const Code = require('@hapi/code'); const Lab = require('@hapi/lab'); const { expect } = Code; const { after, before, describe, it } = exports.lab = Lab.script(); describe('math', () => { before(() => {}); after(() => {}); it('returns true when 1 + 1 equals 2', () => { expect(1 + 1).to.equal(2); }); }); ``` -------------------------------- ### Run Tests with npm Test Source: https://github.com/hapijs/lab/blob/master/API.md Execute the configured test script using the npm test command. This will run Lab with the specified coverage threshold. ```bash $ npm test ``` -------------------------------- ### Execute Lab with TypeScript and tsconfig-paths Source: https://github.com/hapijs/lab/blob/master/API.md Run Lab tests with TypeScript support and enable path resolution for custom configurations by requiring 'tsconfig-paths/register'. ```bash $ lab --typescript --require 'tsconfig-paths/register' ``` -------------------------------- ### Configure lab with .labrc.js for coverage and linting Source: https://github.com/hapijs/lab/blob/master/API.md Centralize lab settings like coverage, threshold, and linting in a `.labrc.js` file. This configuration file can be located in the current directory, a parent directory, or the user's home directory. ```javascript module.exports = { coverage: true, threshold: 90, lint: true }; ``` -------------------------------- ### Enable TypeScript Support in Lab Source: https://github.com/hapijs/lab/blob/master/API.md Use the '--typescript' CLI option to run test files written in TypeScript. A TypeScript definition file is included with Lab. ```typescript import * as Lab from '@hapi/lab'; import { expect } from '@hapi/code'; const lab = Lab.script(); const { describe, it, before } = lab; export { lab }; describe('experiment', () => { before(() => {}); it('verifies 1 equals 1', () => { expect(1).to.equal(1); }); }); ``` -------------------------------- ### script.it(title, [options], test) Source: https://github.com/hapijs/lab/blob/master/API.md Defines a test case, identical to script.test(). ```APIDOC ## script.it(title, [options], test) ### Description Same as [`script.test()`](#scripttesttitle-options-test). ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - `title` - the test description. - `options` - optional flags as describe in [`script.test()`](#scripttesttitle-options-test). - `test` - a sync or async function using the signature `function(done)` where `done` is a callback function to call when the test is complete. ``` -------------------------------- ### Alternative Test Structure in Lab Source: https://github.com/hapijs/lab/blob/master/API.md An alternative way to set up a test file using Lab, assigning the script interface to a variable. Requires @hapi/lab and @hapi/code. ```javascript const Code = require('@hapi/code'); const Lab = require('@hapi/lab'); const { expect } = Code; const lab = exports.lab = Lab.script(); lab.test('returns true when 1 + 1 equals 2', () => { expect(1 + 1).to.equal(2); }); ``` -------------------------------- ### script.experiment(title, [options], content) Source: https://context7.com/hapijs/lab/llms.txt Organizes related tests into a group called an experiment. This method is aliased as `describe` (BDD) and `suite` (TDD). The content function must be synchronous. Options include `skip`, `only`, and `timeout`. ```APIDOC ## script.experiment(title, [options], content) ### Description Organizes related tests into a group called an experiment. Aliased as `describe` (BDD) and `suite` (TDD). The `content` function must be synchronous. Options include `skip`, `only`, and `timeout`. ### Method `script.experiment(title, [options], content)` ### Parameters #### Path Parameters - **title** (string) - Required - The title of the experiment. - **options** (object) - Optional - Configuration for the experiment. Supported keys: `skip` (boolean), `only` (boolean), `timeout` (number in milliseconds). - **content** (function) - Required - A synchronous function containing the tests for this experiment. ### Request Example ```javascript const Code = require('@hapi/code'); const Lab = require('@hapi/lab'); const { expect } = Code; const lab = exports.lab = Lab.script(); lab.experiment('Math utilities', () => { lab.experiment('addition', () => { lab.test('returns correct sum for positive numbers', () => { expect(2 + 3).to.equal(5); }); lab.test('handles negative numbers', () => { expect(-1 + 1).to.equal(0); }); }); lab.experiment('division', { timeout: 500 }, () => { lab.test('throws on divide by zero', () => { const divide = (a, b) => { if (b === 0) { throw new Error('Division by zero'); } return a / b; }; expect(() => divide(10, 0)).to.throw('Division by zero'); }); }); }); ``` ``` -------------------------------- ### Configure Multiple Reporters in .labrc.js Source: https://github.com/hapijs/lab/blob/master/API.md Define multiple reporters and their output paths within a `.labrc.js` configuration file. ```javascript module.exports = { reporter: ['console', 'html', 'lcov', 'json'], output: ['stdout', 'coverage.html', 'lcov.info', 'data.json'] }; ``` -------------------------------- ### Lab Experiment with Basic Test Source: https://github.com/hapijs/lab/blob/master/API.md Organizes tests into experiments using Lab. Includes a simple test case within the experiment. ```javascript lab.experiment('math', () => { lab.test('returns true when 1 + 1 equals 2', () => { expect(1 + 1).to.equal(2); }); }); ``` -------------------------------- ### script.beforeEach() Source: https://github.com/hapijs/lab/blob/master/API.md Executes the provided action before each test is executed in the current experiment block. ```APIDOC ## script.beforeEach() ### Description Executes the provided action before each test is executed in the current experiment block. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - `options` - optional flags as describe in [`script.test()`](#scripttesttitle-options-test). - `action` - a sync or async function using the signature `function(flags)` where `flags` see [Flags](#flags). ``` -------------------------------- ### CLI Usage for Running Tests Source: https://context7.com/hapijs/lab/llms.txt Common CLI patterns for CI pipelines, including coverage thresholds, linting, reporters, test filtering, and shuffling. ```bash # Run all tests in ./test with 100% coverage threshold $ lab -t 100 # Run with coverage + linting enabled, output to console $ lab -c -L # Generate HTML coverage report $ lab -r html -o coverage.html # Run only tests matching a pattern $ lab -g "authentication" # Run tests by ID range $ lab -dv # dry run to print all tests with IDs $ lab -i 1-3,5 # then run only tests 1, 2, 3, and 5 # Multiple reporters: console to stdout + JSON to file $ lab -r console -o stdout -r json -o results.json # Shuffle test order with a fixed seed for reproducibility $ lab --shuffle --seed 42 # Run with bail: stop on first failure $ lab --bail # Run linting only (dry run + lint) $ lab -dL # Silence output, set custom NODE_ENV $ lab -s -e production # ESM projects: use c8 for coverage (lab coverage doesn't support ESM) $ c8 --100 lab -a @hapi/code ``` -------------------------------- ### script.after([options], action) Source: https://github.com/hapijs/lab/blob/master/API.md Executes the provided action after the current experiment block is finished. ```APIDOC ## script.after([options], action) ### Description Executes the provided action after the current experiment block is finished. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - `options` - optional flags as describe in [`script.test()`](#scripttesttitle-options-test). - `action` - a sync or async function using the signature `function(flags)` where `flags` see [Flags](#flags). ``` -------------------------------- ### Execute Lab with TypeScript Source: https://github.com/hapijs/lab/blob/master/API.md Run tests written in TypeScript using the '--typescript' CLI option. ```bash $ lab --typescript ``` -------------------------------- ### Define Tests with Plan, Retry, and Custom Timeout Source: https://context7.com/hapijs/lab/llms.txt Defines individual tests using `lab.test()`. Shows how to specify an exact assertion count with `plan`, enable automatic retries with `retry`, and set a custom `timeout`. ```javascript const Code = require('@hapi/code'); const Lab = require('@hapi/lab'); const { expect } = Code; const lab = exports.lab = Lab.script(); // Test with plan (assert exact number of assertions run) lab.test('validates response shape', { plan: 3 }, async () => { const response = await Promise.resolve({ status: 200, body: 'ok', headers: {} }); expect(response.status).to.equal(200); expect(response.body).to.equal('ok'); expect(response.headers).to.be.an.object(); }); // Test marked for automatic retry on flakiness (up to 5 times by default) lab.test('flaky network operation', { retry: true }, async () => { const result = await fetch('https://example.com/health'); expect(result.ok).to.be.true(); }); // Test with custom timeout lab.test('slow operation completes', { timeout: 5000 }, async () => { await new Promise((resolve) => setTimeout(resolve, 3000)); expect(true).to.be.true(); }); ``` -------------------------------- ### script.test(title, [options], test) Source: https://github.com/hapijs/lab/blob/master/API.md Defines a test case with a title, optional settings, and a test function. Options include skipping, retrying, setting timeouts, and planning assertions. The test function can throw an error or return a Promise. ```APIDOC ## `script.test(title, [options], test)` ### Description Sets up a test where: - `title` - the test description. - `options` - optional settings: - `skip` - if `true`, sets the entire experiment content to be skipped during execution. Defaults to `false`. - `only` - if `true`, sets all other experiments to `skip`. Default to `false`. - `timeout` - overrides the default test timeout for tests and other timed operations in milliseconds. Defaults to `2000`. - `plan` - the expected number of assertions the test must execute. This setting should only be used with an assertion library that supports a `count()` function, like [**code**](https://hapi.dev/family/code). - `retry` - when `true` or set to a number greater than `0`, if the test fails it will be retried `retries` (defaults to `5`) number of times until it passes. - `test` - a function with signature `function(flags)` where: - the function can throw if the test failed. - the function can return a Promise which either resolves (success) or rejects (fails). - all other return value is ignored. - `flags` - a set of test utilities described in [Flags](#flags). ### Request Example ```javascript lab.experiment('my plan', () => { lab.test('only a single assertion executes', { plan: 1 }, () => { expect(1 + 1).to.equal(2); }); }); ``` ``` -------------------------------- ### Configure Multiple Reporters via CLI Source: https://github.com/hapijs/lab/blob/master/API.md Specify multiple reporters using the `-r` flag. Output paths can be defined using the `-o` flag, matching the order of reporters. ```bash $ lab -r console -r html ``` ```bash $ lab -r console -o stdout -r html -o coverage.html -r lcov -o lcov.info -r json -o data.json ``` ```bash $ lab -r console -o stdout -r console -o console.log ``` -------------------------------- ### Asynchronous Test in Lab Source: https://github.com/hapijs/lab/blob/master/API.md Demonstrates an asynchronous test using async/await within Lab. The test performs a file read and assertion. ```javascript lab.test('config file has correct value', async () => { const file = await fs.readFile('config'); expect(file.toString()).to.contain('something'); }); ``` -------------------------------- ### .labrc.js Configuration File Source: https://context7.com/hapijs/lab/llms.txt Persist default settings in a .labrc.js file in the project root. CLI flags override these settings. ```javascript // .labrc.js module.exports = { coverage: true, threshold: 100, lint: true, 'lint-errors-threshold': 0, 'lint-warnings-threshold': 0, reporter: ['console', 'lcov'], output: ['stdout', 'lcov.info'], paths: ['test'], timeout: 5000, 'context-timeout': 2000, globals: ['MyGlobal', 'Symbol(special)'], 'coverage-exclude': ['src/vendor', 'src/generated'], 'coverage-predicates': { win32: process.platform === 'win32' } }; ``` -------------------------------- ### Lab Experiment with .only() Option Source: https://github.com/hapijs/lab/blob/master/API.md Demonstrates how to use the `.only()` method on an experiment or test to ensure only that specific test or experiment runs, skipping others. ```javascript lab.experiment('with only', () => { lab.test.only('only this test will run', () => { expect(1 + 1).to.equal(2); }); lab.test('another test that will not be executed', () => {}); }); ``` -------------------------------- ### Test Driven Development (TDD) Style in Lab Source: https://github.com/hapijs/lab/blob/master/API.md Sets up Lab to follow a Test Driven Development (TDD) style using `suite` and `test` functions. ```javascript const Code = require('@hapi/code'); const Lab = require('@hapi/lab'); const { expect } = Code; const { suite, test } = exports.lab = Lab.script(); suite('math', () => { test('returns true when 1 + 1 equals 2', () => { expect(1 + 1).to.equal(2); }); }); ``` -------------------------------- ### Integrate @hapi/code Assertion Library with Lab Source: https://github.com/hapijs/lab/blob/master/API.md Use the `--assert @hapi/code` CLI argument or the `assert` option in `Lab.script()` to integrate @hapi/code. This allows for using `expect` and `fail` from @hapi/code within your tests. ```javascript const lab = exports.lab = Lab.script(); const { describe, it } = lab; // Testing shortcuts const { expect, fail } = require('@hapi/code'); describe('expectation', () => { it('should be able to expect', () => { expect(true).to.be.true(); }); it('should be able to fail (This test should fail)', () => { fail('Should fail'); }); }); ``` ```bash $ lab --assert @hapi/code ``` -------------------------------- ### Configure test paths in .labrc.js Source: https://github.com/hapijs/lab/blob/master/API.md Use the `paths` parameter in `.labrc.js` to specify custom directories for test files, overriding the default `./test` directory. ```javascript module.exports = { paths: ['test/lab'], }; ``` -------------------------------- ### Per-test lifecycle hooks with script.beforeEach and script.afterEach Source: https://context7.com/hapijs/lab/llms.txt Run before or after every individual test in the current experiment. `context` is shallow-cloned per test to prevent state leakage. ```javascript const Code = require('@hapi/code'); const Lab = require('@hapi/lab'); const { expect } = Code; const lab = exports.lab = Lab.script(); lab.experiment('HTTP handler', () => { lab.beforeEach(({ context }) => { context.req = { method: 'GET', headers: {}, params: {} }; context.res = { statusCode: 200, body: null }; }); lab.afterEach(({ context }) => { // Cleanup per-test resources context.req = null; context.res = null; }); lab.test('handles GET /users', ({ context }) => { context.req.params.resource = 'users'; expect(context.req.method).to.equal('GET'); expect(context.res.statusCode).to.equal(200); }); lab.test('handles missing resource gracefully', ({ context }) => { expect(context.req.params.resource).to.be.undefined(); }); }); ``` -------------------------------- ### Lab.script([options]) Source: https://context7.com/hapijs/lab/llms.txt Creates a test script interface that is used to register experiments and tests. This function must be assigned to exports.lab for the CLI runner to detect it. The schedule option controls auto-execution when run directly. ```APIDOC ## Lab.script([options]) ### Description Creates a test script interface used to register experiments and tests. Must be assigned to `exports.lab` for the CLI to detect it. The optional `options.schedule` flag (default `true`) controls whether the script auto-executes when run directly with Node.js; set it to `false` when using Lab programmatically. ### Method `Lab.script([options])` ### Parameters #### Options - **schedule** (boolean) - Optional - Controls whether the script auto-executes when run directly with Node.js. Defaults to `true`. ### Request Example ```javascript const Code = require('@hapi/code'); const Lab = require('@hapi/lab'); const { expect } = Code; const lab = exports.lab = Lab.script(); // Basic sync test lab.test('adds two numbers correctly', () => { expect(1 + 1).to.equal(2); }); // Async test lab.test('resolves a promise', async () => { const result = await Promise.resolve('hello'); expect(result).to.equal('hello'); }); ``` ``` -------------------------------- ### script.test(title, [options], fn) Source: https://context7.com/hapijs/lab/llms.txt Defines a single test case. This method is aliased as `it`. The test function receives a `flags` object. Options include `skip`, `only`, `timeout`, `plan` (expected assertion count), and `retry`. ```APIDOC ## script.test(title, [options], fn) ### Description Defines a single test case. Aliased as `it`. The function receives a `flags` object. Options include `skip`, `only`, `timeout`, `plan` (expected assertion count), and `retry`. ### Method `script.test(title, [options], fn)` ### Parameters #### Path Parameters - **title** (string) - Required - The title of the test. - **options** (object) - Optional - Configuration for the test. Supported keys: `skip` (boolean), `only` (boolean), `timeout` (number in milliseconds), `plan` (number), `retry` (boolean or number). - **fn** (function) - Required - The test function to execute. It can be synchronous or asynchronous. ### Request Example ```javascript const Code = require('@hapi/code'); const Lab = require('@hapi/lab'); const { expect } = Code; const lab = exports.lab = Lab.script(); // Test with plan (assert exact number of assertions run) lab.test('validates response shape', { plan: 3 }, async () => { const response = await Promise.resolve({ status: 200, body: 'ok', headers: {} }); expect(response.status).to.equal(200); expect(response.body).to.equal('ok'); expect(response.headers).to.be.an.object(); }); // Test marked for automatic retry on flakiness (up to 5 times by default) lab.test('flaky network operation', { retry: true }, async () => { const result = await fetch('https://example.com/health'); expect(result.ok).to.be.true(); }); // Test with custom timeout lab.test('slow operation completes', { timeout: 5000 }, async () => { await new Promise((resolve) => setTimeout(resolve, 3000)); expect(true).to.be.true(); }); ``` ``` -------------------------------- ### Run only linting with lab -dL Source: https://github.com/hapijs/lab/blob/master/API.md Combine the `dry` run flag (`d`) with the `lint` flag (`L`) to execute only the linting process without running tests. ```sh lab -dL ``` -------------------------------- ### TDD style tests with suite/test Source: https://context7.com/hapijs/lab/llms.txt Lab provides suite/test as aliases for experiment/test, enabling familiar TDD syntax without extra configuration. ```javascript // TDD style const { suite, test } = exports.lab = Lab.script(); suite('Calculator', () => { test('multiply returns correct product', () => { expect(3 * 4).to.equal(12); }); }); ``` -------------------------------- ### script.afterEach() Source: https://github.com/hapijs/lab/blob/master/API.md Executes the provided action after each test is executed in the current experiment block. ```APIDOC ## script.afterEach() ### Description Executes the provided action after each test is executed in the current experiment block. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - `options` - optional flags as describe in [`script.test()`](#scripttesttitle-options-test). - `action` - a sync or async function using the signature `function(flags)` where `flags` see [Flags](#flags). ``` -------------------------------- ### Programmatic API: Lab.report and Lab.execute Source: https://context7.com/hapijs/lab/llms.txt Run Lab programmatically without the CLI. `report` runs tests and finalizes output; `execute` returns raw results. Use `schedule: false` in `Lab.script()` to suppress auto-execution. ```javascript const Lab = require('@hapi/lab'); const Code = require('@hapi/code'); const { expect } = Code; // Build the script without auto-scheduling const script = Lab.script({ schedule: false }); script.experiment('programmatic run', () => { script.test('always passes', () => { expect(true).to.be.true(); }); script.test('async check', async () => { const val = await Promise.resolve(42); expect(val).to.equal(42); }); }); // Run and collect results Lab.report(script, { output: process.stdout, reporter: 'console', coverage: false, leaks: false, threshold: 0 }).then((result) => { if (result) { process.exit(1); // non-zero indicates failures } }); ``` -------------------------------- ### Group Tests into Experiments with Nested Structures Source: https://context7.com/hapijs/lab/llms.txt Organizes related tests into experiments using `lab.experiment()`. Demonstrates nested experiments and tests with specific options like `timeout`. ```javascript const Code = require('@hapi/code'); const Lab = require('@hapi/lab'); const { expect } = Code; const lab = exports.lab = Lab.script(); lab.experiment('Math utilities', () => { lab.experiment('addition', () => { lab.test('returns correct sum for positive numbers', () => { expect(2 + 3).to.equal(5); }); lab.test('handles negative numbers', () => { expect(-1 + 1).to.equal(0); }); }); lab.experiment('division', { timeout: 500 }, () => { lab.test('throws on divide by zero', () => { const divide = (a, b) => { if (b === 0) { throw new Error('Division by zero'); } return a / b; }; expect(() => divide(10, 0)).to.throw('Division by zero'); }); }); }); ``` -------------------------------- ### Update Test Command for c8 Integration Source: https://github.com/hapijs/lab/blob/master/API.md Modify your package.json test script to use c8 to run Lab tests with code coverage. This replaces Lab's native coverage with c8's. ```diff "scripts": { - "test": "lab -a @hapi/code -t 100" + "test": "c8 --100 lab -a @hapi/code" }, ``` -------------------------------- ### Configure ESLint ignores in eslint.config.js Source: https://github.com/hapijs/lab/blob/master/API.md Extend the default Hapi ESLint configuration and specify files to ignore in linting by adding an `ignores` rule to your `eslint.config.js` file. ```javascript import HapiPlugin from '@hapi/eslint-plugin'; export default [ { ignores: ['node_modules/*', '**/vendor/*.js'], }, ...HapiPlugin.configs.module, ]; ``` -------------------------------- ### Define a Test with a Plan in Hapi Lab Source: https://github.com/hapijs/lab/blob/master/API.md Use `script.test` with the `plan` option to specify the expected number of assertions. This is useful when using assertion libraries that support counting. ```javascript lab.experiment('my plan', () => { lab.test('only a single assertion executes', { plan: 1 }, () => { expect(1 + 1).to.equal(2); }); }); ``` -------------------------------- ### Configure Test Timeouts in Lab Source: https://github.com/hapijs/lab/blob/master/API.md Set specific timeouts for experiments or individual hooks like before(), after(), beforeEach(), and afterEach() using the 'timeout' option in milliseconds. Disabled by default or uses the value of -M. ```javascript lab.experiment('math', { timeout: 1000 }, () => { lab.before({ timeout: 500 }, () => { doSomething(); }); lab.test('returns true when 1 + 1 equals 2', () => { expect(1 + 1).to.equal(2); }); }); ``` -------------------------------- ### Override .labrc.js settings with command-line flags Source: https://github.com/hapijs/lab/blob/master/API.md Command-line options passed to the lab runner will override settings defined in the `.labrc.js` file. This allows for flexible adjustments on a per-run basis. ```sh lab -t 80 ``` -------------------------------- ### Manage Context Across Nested Experiments in Hapi Lab Source: https://github.com/hapijs/lab/blob/master/API.md Utilize `lab.before` to set properties on the `context` object, which is passed to tests and child experiments. The context is shallow cloned, allowing modifications in nested experiments without affecting parent or sibling contexts. ```javascript lab.experiment('my experiment', () => { lab.before(({ context }) => { context.foo = 'bar'; }) lab.test('contains context', ({ context }) => { expect(context.foo).to.equal('bar'); }); lab.experiment('a nested experiment', () => { lab.before(({ context }) => { context.foo = 'baz'; }); lab.test('has the correct context', ({ context }) => { expect(context.foo).to.equal('baz'); // since this is a shallow clone, changes will not be carried to // future tests or experiments context.foo = 'fizzbuzz'; }); lab.test('receives a clean context', ({ context }) => { expect(context.foo).to.equal('baz'); }); }); lab.experiment('another nested experiment', () => { lab.test('maintains the original context', ({ context }) => { expect(context.foo).to.equal('bar'); }); }); }); ``` -------------------------------- ### Register cleanup function with onCleanup Source: https://context7.com/hapijs/lab/llms.txt Assign an async or sync function to flags.onCleanup to ensure it runs after the test completes, even on timeout or error. This replaces try/finally patterns for resource teardown. ```javascript const Code = require('@hapi/code'); const Lab = require('@hapi/lab'); const { expect } = Code; const lab = exports.lab = Lab.script(); lab.test('writes and cleans up a temp file', async (flags) => { const Fs = require('fs').promises; const tmpFile = '/tmp/lab-test-output.txt'; await Fs.writeFile(tmpFile, 'test data'); flags.onCleanup = async () => { await Fs.unlink(tmpFile).catch(() => {}); // Always runs }; const content = await Fs.readFile(tmpFile, 'utf8'); expect(content).to.equal('test data'); // onCleanup fires here even if expect throws }); ``` -------------------------------- ### Attach notes to test output with note Source: https://context7.com/hapijs/lab/llms.txt Use flags.note to append string notes to the test's console reporter output. This is useful for surfacing dynamic values in CI output. ```javascript const Code = require('@hapi/code'); const Lab = require('@hapi/lab'); const { expect } = Code; const lab = exports.lab = Lab.script(); lab.test('performance benchmark', async (flags) => { const start = Date.now(); await heavyComputation(); const duration = Date.now() - start; flags.note(`Computation took ${duration}ms`); flags.note(`Timestamp: ${new Date().toISOString()}`); expect(duration).to.be.below(1000); }); ``` -------------------------------- ### script.test.skip(title, [options], test) Source: https://github.com/hapijs/lab/blob/master/API.md A convenience method equivalent to calling `script.test()` with the `skip` option set to `true`, preventing this test from running. ```APIDOC ## `script.test.skip(title, [options], test)` ### Description Same as calling [`script.test()`](#scripttesttitle-options-test) with `skip` option set to `true`. ``` -------------------------------- ### TypeScript Tests with Built-in Transpiler Source: https://context7.com/hapijs/lab/llms.txt Run TypeScript tests using the --typescript flag. For custom tsconfig path aliases, use --require tsconfig-paths/register. ```typescript // test/auth.ts import * as Lab from '@hapi/lab'; import { expect } from '@hapi/code'; const lab = Lab.script(); const { describe, it, before } = lab; export { lab }; interface User { id: number; name: string; } describe('Auth module', () => { let token: string; before(() => { token = 'abc123'; }); it('issues a non-empty token', () => { expect(token).to.be.a.string().and.to.have.length.greaterThan(0); }); it('rejects invalid tokens', () => { const verify = (t: string): boolean => t === token; expect(verify('wrong')).to.be.false(); }); }); ``` ```bash # Run TypeScript tests with built-in transpiler $ lab --typescript # With custom tsconfig path aliases $ lab --typescript --require tsconfig-paths/register ``` -------------------------------- ### script.test.only(title, [options], fn) Source: https://context7.com/hapijs/lab/llms.txt Marks a specific test to run exclusively, skipping all other tests and experiments within the same script. This is useful for isolating and debugging a particular test case during development. ```APIDOC ## script.test.only(title, [options], fn) ### Description Marks a specific test to run exclusively, skipping all other tests and experiments within the same script. This is useful for isolating and debugging a particular test case during development. ### Method `script.test.only(title, [options], fn)` ### Parameters #### Path Parameters - **title** (string) - Required - The title of the test. - **options** (object) - Optional - Configuration for the test. Supported keys: `skip` (boolean), `only` (boolean), `timeout` (number in milliseconds), `plan` (number), `retry` (boolean or number). - **fn** (function) - Required - The test function to execute. It can be synchronous or asynchronous. ### Request Example ```javascript const Code = require('@hapi/code'); const Lab = require('@hapi/lab'); const { expect } = Code; const lab = exports.lab = Lab.script(); lab.experiment('authentication', () => { lab.test.only('focuses on this specific case', () => { // Only this test will execute; all others are skipped expect('token').to.be.a.string(); }); lab.test('this test will be skipped', () => { expect(1).to.equal(1); }); }); ``` ``` -------------------------------- ### Shared test context object with flags.context Source: https://context7.com/hapijs/lab/llms.txt An object passed to `before`, `after`, `beforeEach`, `afterEach`, and test functions. It is shallow-cloned at each experiment level so nested experiments can override values without affecting siblings or parents. ```javascript const Code = require('@hapi/code'); const Lab = require('@hapi/lab'); const { expect } = Code; const lab = exports.lab = Lab.script(); lab.experiment('context scoping', () => { lab.before(({ context }) => { context.config = { env: 'test', retries: 3 }; }); lab.test('reads parent context', ({ context }) => { expect(context.config.env).to.equal('test'); }); lab.experiment('nested scope', () => { lab.before(({ context }) => { // Overrides only for this nested experiment context.config = { env: 'staging', retries: 1 }; }); lab.test('reads overridden context', ({ context }) => { expect(context.config.env).to.equal('staging'); }); }); lab.test('parent context is unchanged', ({ context }) => { expect(context.config.env).to.equal('test'); }); }); ``` -------------------------------- ### Test uncaught exceptions with onUncaughtException Source: https://github.com/hapijs/lab/blob/master/API.md Use `flags.onUncaughtException` to override global exception handling for testing code that should throw uncaught exceptions. Ensure the error matches expectations and resolve the promise to complete the test. ```javascript lab.test('leaves an uncaught rejection', (flags) => { return new Promise((resolve) => { flags.onUncaughtException = (err) => { expect(err).to.be.an.error('I want this exception to remain uncaught in production'); resolve(); // finish the test }; // sample production code setTimeout(() => { throw new Error('I want this exception to remain uncaught in production'); }); }); }); ```