LPM CLI

lpm test

Run project tests with Vitest, Jest, Mocha, or a package script, including selected workspace members.

Use lpm test to run the test runner declared by your project.

lpm test [OPTIONS] [ARGS]...

Quickstart

lpm install -D vitest
lpm test
lpm test -- --watch
ModeCommand
Run the current packagelpm test
Run every workspace memberlpm test --all
Run selected memberslpm test --filter web
Run affected memberslpm test --affected --base main
Get a JSON resultlpm --json test

Runner selection

LPM CLI reads dependencies and devDependencies in the package manifest. It checks these runners in order:

  1. Vitest: vitest run.
  2. Jest: jest.
  3. Mocha: mocha.
  4. The test script in package.json.

A declared runner takes priority over the package script. If the selected runner has no installed executable, run lpm install. Detection does not download a runner automatically.

To select the package script directly, use lpm run test. The dedicated test command does not use task commands, task caching, or task dependencies from lpm.json.

Project configuration

lpm.json has no dedicated test section. Runtime pins and project environment configuration still apply.

lpm.json
{
  "runtime": {
    "node": "22"
  },
  "env": {
    "test": ".env.test"
  }
}

tasks.test.env selects the environment for detected test runners and package scripts first. If that field is absent, env.test selects the environment file. A task environment can apply even though tasks.test.command does not replace the package script. The script also uses its pretest and posttest hooks and npm lifecycle variables. A failed hook stops the later phases. Forwarded arguments apply to the main script.

Both execution paths load the project environment and enforce its environment schema. The command has no dedicated --env option. See environment files and lpm run for named script environments.

LPM CLI prepares configured Node and Bun versions before the test run. Workspace members inherit root runtime pins and can override each runtime locally. It checks engines.node against the Node executable on the test PATH. A strict engine mismatch stops the run.

If a configured runtime cannot be installed, LPM CLI warns and uses the first matching executable on PATH. Use lpm use to install the required version. See managed runtimes for runtime selection and automatic downloads.

Coverage and CI

lpm install -D vitest @vitest/coverage-v8
lpm test -- --coverage
lpm --json test --all

Vitest runs once by default. Jest and Mocha receive their arguments directly. A package script controls its own test behavior. For CI, use a script that exits after its test run.

Put runner options after -- to keep them separate from LPM CLI options. For example, Jest's --all belongs after the delimiter:

lpm test -- --all
lpm test --filter web -- --reporter=verbose

Watch mode

lpm test -- --watch
lpm test --filter web -- --watch

For Vitest watch mode, LPM CLI removes the default run argument. Jest and Mocha receive their watch options directly. Jest's -w <N> sets its worker count. It does not request watch mode.

A workspace selection can watch exactly one member. LPM CLI rejects a watch request that selects no members or multiple members. Jest's --watchAll also requests watch mode.

Vitest short-option bundles such as -uw also request watch mode. Tokens after a second -- belong to the runner and do not select watch mode.

On Unix, Ctrl+C or a stop signal stops active runners and their tracked descendants. A daemon that detaches and outlives its parent manages its own shutdown. LPM CLI gives child cleanup handlers time to run before it forces termination. A stopped run does not start later hooks.

For disabled Vitest watch mode, use --watch=false. Values such as --watch=off and --watch=0 enable watch mode in Vitest.

Workspace selection

lpm test --all
lpm test --filter '@scope/*'
lpm test --filter './apps/*'
lpm test --filter web --filter api
lpm test --filter-prod ...shared
lpm test --affected --base origin/main
lpm test --filter web --fail-if-no-match
lpm test --all --workspace-concurrency 2

Detection runs separately for each selected member. A workspace can use different runners in different members. The command reports a missing runner as a member failure.

--all cannot combine with filters or --affected. Filters and --affected combine as a union. --filter-prod excludes development edges from dependency closures.

Members run in dependency order. Independent members can run together. A failed member does not stop the remaining selected members. --workspace-concurrency limits the number of selected members that run at once. The limit uses the CLI flag, then root lpm.toml [workspace].concurrency, then global workspace-concurrency, then available parallelism. This limit does not control each runner's internal workers. See workspace filters.

JSON output and failures

lpm --json test
lpm --json test --filter web

Both single-package and workspace runs emit one JSON result. The result includes each member's status. Failed members include captured output or a launch error. Successful members do not include their captured output.

A single-package run preserves the runner or hook exit code. If any selected member fails, a workspace run exits with status 1. Each failed member retains its own exit code. On Unix, interrupted runs exit with 130 for SIGINT or 143 for SIGTERM.

A launch or configuration failure has exit_code: null and an error message. Failures before member preparation use the global error object. Captured output has a 10 MiB limit per stream. JSON mode closes stdin and emits its result after completion.

If a child leaves output pipes open, LPM CLI bounds the final drain and cleans up the owned process groups. Background services from a package pre-hook remain available through the main script and post-hook. In JSON mode, cleanup occurs after the script sequence.

Flags

FlagEffect
--allSelect every workspace member
--filter <FILTER>Select members with the workspace filter grammar. Repeat to combine selections
--filter-prod <FILTER_PROD>Select members with production dependency closures
--affectedSelect members affected by Git changes
--base <BASE>Set the Git base for --affected. Default: main
--changed-files-ignore-pattern <CHANGED_FILES_IGNORE_PATTERN>Ignore matching changed paths during Git-based selection
--test-pattern <TEST_PATTERN>Treat matching changed paths as test-only during affected selection
--fail-if-no-matchIf no workspace member matches, fail
--workspace-concurrency <N>Limit concurrent workspace members

Runner flags belong after --. See global flags.

See also