The
uvuCLI is available whenever you install theuvupackage.
The role of the uvu CLI is to collect and execute your tests suites. In order to do that, you must tell it how/where to find your tests. Otherwise, it has a default set of file patterns to search for – but that probably won't align with your project's structure.
Note: Using the
uvuCLI is actually optional! See Isolation for more info.
Let's take a look at the CLI's help text:
$ uvu --help
#
# Usage
# $ uvu [dir] [pattern] [options]
#
# Options
# -b, --bail Exit on first failure
# -i, --ignore Any file patterns to ignore
# -r, --require Additional module(s) to preload
# -C, --cwd The current directory to resolve from (default .)
# -c, --color Print colorized output (default true)
# -v, --version Displays current version
# -h, --help Displays this message
#As you can see, there are few arguments and option flags!
They're categorized by responsibility:
- where/how to locate test files (
dir,pattern,--ignore, and--cwd) - environment preparation (
--requireand--color) - whether or not to exit on suite failures (
--bail)
Aside: Glob Patterns
Unlike other test runners, uvu intentionally does not rely on glob patterns.
Parsing and matching globs both require a non-trivial amount of work. (Even the smallest glob-parsers are as large – or larger – than the entire uvu source!) This price has to be paid upfront – at startup – and then becomes completely irrelevant. And then on the consumer side, glob patterns fall into one of two categories:
- extremely simple patterns (eg,
test/**,tests/**/*.test.(ts|js), etc) - extremely complicated patterns that require squinting and fine-tuning
Simple patterns don't require a glob pattern 99% of the time.
Complicated glob patterns can (often) be better expressed or understood as separate segments.
And then there's the reoccurring issue of some shells and/or operating systems (eg, Windows) that will pre-evaluate a glob pattern, ruining a CLI's expected input... And how different systems require quotation marks, and/or certain characters to be escaped... There are issues with this approach. It certainly can work, but uvu sidesteps this can of worms in favor of a different, but simpler approach.
Logic
The CLI will always resolve lookups from the --cwd location, which is the process.cwd() when unspecified.
By default (aka, when no arguments are given), uvu looks for files matching this behemoth of a pattern:
/((\/|^)(tests?|__tests?__)\/.*|\.(tests?|spec)|^\/?tests?)\.([mc]js|[jt]sx?)$/i;This will:
- search within
test,testsor__test__or__tests__directories if they exist; otherwise any directory - search for files matching
*.test.{ext}or*.tests.{ext}or*.spec.{ext} - search for files with these extensions:
mjs,cjs,js,jsx,tsx, orts
Of course, you can help uvu out and narrow down its search party by providing a dir argument. Instead of searching from your project's root, dir tells uvu where to start its traversal from. And by specifying a dir, the default pattern changes such that any file with a mjs, cjs, js, jsx, tsx, or ts extension will match.
Finally, you may specify your own pattern too — assuming dir has been set.
The pattern value is passed through new RegExp(<pattern>, 'i'). If you are nostalgic for complex glob patterns, this is your chance to relive its glory days – with substitutions, of course.
For example, if we assume a monorepo environment, your uvu usage may look like this:
$ uvu packages tests -i fixturesThis will traverse the packages directory, looking at files and subdirectories that match /tests/i, but ignore anything that matches the /fixtures/i pattern.
Ignoring
Files inside any node_modules directory are always ignored.
You can use the -i/--ignore flags to provide additional patterns to ignore. Much like [pattern], these values are cast to a RegExp, allowing you to be as vague or specific as you need to be.
The -i/--ignore flags can be passed multiple times:
# Traverse "packages" diectory
# ~> ignore items matching /foobar/i
# ~> ignore items matching /fixtures/i
# ~> ignore items matching /\d+.js$/i
$ uvu packages -i foobar -i fixtures -i \\d+.js$When running uvu, it looks for all test files and will enqueue all suites for execution. You can always disable individual suites (by commenting out its .run() call), but sometimes you just want to execute a single file.
To do this, you can simply call node with the path to your file! 🎉
This works because uvu test files are self-contained – its import/require statements aren't tucked away, nor are the suites' run() invocation.
# Before (runs everything in packages/**/**test**)
$ uvu packages test
# After (specific file)
$ node packages/utils/test/random.jsSince uvu and node share the --require hook, you can bring your uvu -r arguments to node too~!
# Before
$ uvu -r esm tests
$ uvu -r ts-node/register tests
# After
$ node -r esm tests/math.js
$ node -r ts-node/register tests/math.ts