Skip to content

Repository files navigation

Leia

Leia is a testing utility that tests code blocks in documentation. This makes tests easy to write and also ensures documentation is up to date and working. Behind the scenes documentation is parsed and run as a series of mocha tests.

Leia will

  • Consolidate code examples and tests into a single, easy to understand and write markdown file
  • Write functional tests quickly in an accessible and lowest common denominator language (eg sh/bash/dash etc)
  • Pass on exit status code 0, fail on anything else
  • Work cross platform-ish, with some caveats, see Shell Considerations below
  • Keep Lando honest so he can be a real hero who doesn't betray his friends again

Installation

Leia 1.x requires Node.js 24 or newer.

# With npm
npm install @lando/leia

Basics

A basic Leia test needs one H1 heading, at least one matching H2 test heading, and a fenced code block. Inside the code block, a comment describes the test and the following line runs its command.

# Some Example

## Testing

```bash
# Should print a greeting
echo "Hello from Leia"
```

Usage

You can invoke leia as a command line tool or directly require it in a module.

CLI

npx leia

Cleverly converts markdown files into mocha cli tests

USAGE
  $ leia <files> <patterns> [--cleanup-header=<cleanup-headers>] [--debug] [--help] [--ignore=<patterns>]
  [--module-format=<auto|commonjs|esm>] [--retry=<count>] [--setup-header=<setup-headers>]
  [--test-header=<test-headers>] [--shell=<bash|cmd|powershell|pwsh|sh|zsh>] [--stdin] [--timeout=<seconds>]
  [--version]

ARGUMENTS
  TESTS  files or patterns to scan for test

OPTIONS
  -c, --cleanup-header=cleanup-header      [default: Clean,Tear,Burn] considers these h2 sections as cleanup commands
  -i, --ignore=ignore                      files or patterns to ignore
  --module-format=auto|commonjs|esm        [default: auto] generates CommonJS or ESM harnesses, autodetected by default
  -r, --retry=retry                        [default: 1] non-negative number of times to retry each test
  -s, --setup-header=setup-header          [default: Start,Setup,This is the dawning] considers these h2 sections as setup commands
  -t, --test-header=test-header            [default: Test,Validat,Verif] considers these h2 sections as tests
  -v, --version                            shows version info
  --debug                                  shows debug output
  --help                                   shows help
  --shell=bash|cmd|powershell|pwsh|sh|zsh  [default: /opt/homebrew/bin/zsh] runs tests with given shell, autodetected by default
  --stdin                                  attachs stdin when the test is run
  --timeout=timeout                        [default: 1800] non-negative whole seconds before tests time out (max 2147483)

EXAMPLES
  leia README.md
  leia README.md "examples/**/*.md" --retry 6 --test-header Tizzestin
  leia "examples/*.md" --ignore BUTNOTYOU.md test --stdin --timeout 5
  leia README.md --shell cmd
  leia README.md --module-format esm

--retry and --timeout accept non-negative integers. Retry counts may not exceed JavaScript's safe-integer limit; timeouts may not exceed 2147483 seconds so their millisecond conversion remains within Node's timer range. Leia rejects invalid, fractional, or out-of-range values before generating or loading a harness.

Module

// Instantiate Leia, which remains a CommonJS package.
const Leia = require('@lando/leia');
const leia = new Leia();

async function main() {
  const files = leia.find(['examples/**.md']);
  const sources = leia.parse(files, {moduleFormat: 'auto'});
  const tests = leia.generate(sources);

  // runAsync loads either CommonJS or ESM harnesses before returning Mocha.
  const runner = await leia.runAsync(tests);
  runner.run((failures) => process.exitCode = failures ? 1 : 0);
}

main().catch(error => {
  console.error(error);
  process.exitCode = 1;
});

The synchronous CommonJS API remains available when the format is explicitly known:

const Leia = require('@lando/leia');
const leia = new Leia();
const files = leia.find(['examples/**.md']);
const sources = leia.parse(files, {moduleFormat: 'commonjs'});
const tests = leia.generate(sources);
const runner = leia.run(tests);
runner.run((failures) => process.exitCode = failures ? 1 : 0);

For more details on specific options check out the code docs

Module formats

--module-format and the programmatic moduleFormat option accept auto, commonjs, or esm. The default auto selection starts from the command's initial working directory and walks upward to the nearest package.json. A package with "type": "module" selects ESM; "type": "commonjs", an absent type, or no package file selects CommonJS. Leia reports an unreadable or malformed nearest package file instead of silently guessing. An explicit format always overrides package detection, and one resolved format applies to every Markdown source in an invocation.

CommonJS harnesses use .leia.cjs; ESM harnesses use .leia.mjs. These extensions make the generated format independent of the temporary directory's enclosing package scope. ESM harnesses load Leia's CommonJS dependencies through Node's createRequire, so Leia's own package and source remain CommonJS. Projects that added a nested package.json containing {"type":"commonjs"} only to protect Leia's old .leia.js output may remove that workaround after upgrading.

Use leia.run() for explicitly CommonJS harnesses. Use await leia.runAsync() for auto or ESM workflows; it supports both formats and loads native ESM through Mocha's asynchronous loader.

Markdown Syntax

For a Markdown file to be recognized as containing functional tests, it needs at least the following:

1. An H1 heading

# Something to identify these tests

2. An H2 test heading

By default, Leia looks for sections beginning with "Testing". These sections contain your tests.

## Testing

Customize the words Leia uses to identify test sections with --test-header. Run npx leia --help to see the defaults.

3. A fenced code block with a comment and command

Under a matching H2 heading, add a fenced code block containing at least one comment and one command. The comment becomes the human-readable test description.

Here is a basic code block that runs one test

# Should cat a file
cat test.txt

For more syntax examples, see the basic executable example.

Skipping

You can also skip tests. This is useful if you want to stub out a test for later.

# Should write this test later and not forget it
skip

Environment Variables

leia will also set the following environment variables for each test that is running so you can use them for stuff.

Here are the values you would expect for the Should set envvars with the test number test in examples/environment.md running on Leia version v1.0.0 with --retry=1.

# generic vars
LEIA=true
LEIA_ENVIRONMENT=true
LEIA_VERSION=1.0.0

# test vars
LEIA_TEST_RUNNING=true
LEIA_TEST_ID=environment
LEIA_TEST_NUMBER=4
LEIA_TEST_RETRY=1
LEIA_TEST_STAGE=test

Note: LEIA_TEST_STAGE can be either setup, test or cleanup and LEIA_TEST_NUMBER resets to 1 for each LEIA_TEST_STAGE.

Shell considerations

When --shell is omitted, leia selects a shell with deterministic platform precedence:

  • On Windows, SHELL wins, followed by MSYSTEM=MINGW64 using bash.exe, COMSPEC, and finally cmd.exe.
  • On macOS and other Unix systems, the account shell from os.userInfo() wins, followed by SHELL. The final fallback is /bin/zsh on macOS and /bin/sh elsewhere.

Unix account lookup failures are reported instead of silently changing the selected shell. An unrecognized selected shell still uses Leia's supported sh behavior.

You can also explicitly tell leia what shell to use with the --shell option. However, currently only bash, sh, zsh, cmd, powershell and pwsh are supported options.

In most use cases it's best to just let leia decide the shell to use automatically.

Advanced Usage

Leia also allows you to specify additional h2 sections in your markdown for setup and cleanup commands that run before and after your core tests. You can tell leia what words these headers should start with in order to be flagged as setup and cleanup commands using the --setup-header and --cleanup-header options.

Here is an example of a markdown file with Setup, Testing and Cleanup sections. And here is a whole directory of examples that we test on every commit.

Issues, Questions and Support

If you have a question or would like some community support we recommend you join us on Slack. Note that this is the Slack community for Lando but we are more than happy to help with this module as well!

If you'd like to report a bug or submit a feature request then please use the issue queue in this repo.

Changelog

User- and developer-visible changes are recorded in the changelog and published release notes.

Development

Leia development requires Node 24 LTS. The root .node-version is the runtime authority for version-aware local tooling and GitHub Actions. See CONTRIBUTING.md for local setup, Lando-based setup, validation, and pull request guidance.

Releasing

To deploy and publish a new version of the package to the npm registry, create a release on GitHub with a semver tag. Every GitHub release publishes to the edge npm tag. Releases not marked as prereleases also move npm's latest tag to the same version, regardless of the version string.

The @lando/leia package must trust the lando/leia GitHub Actions publisher using release.yml. Package publication uses OIDC without an npm token. NPM_DEPLOY_TOKEN is a granular package token used only to update the latest dist-tag, while prepare-release-action synchronizes the version and changelog.

Maintainers

Contributors

Image

Made with contributors-img.

Legacy Version

You can still install the older version of Leia eg leia-parser.

npm install leia-parser

And its documentation lives on here.

Other Resources

About

A testing utility that tests code blocks in documentation.

Topics

Resources

Contributing

Stars

19 stars

Watchers

4 watching

Forks

Releases

Packages

Used by

Contributors

Languages