| scripts | ||
| src | ||
| tests | ||
| vim | ||
| web | ||
| .gitignore | ||
| .npmrc | ||
| Cargo.lock | ||
| Cargo.toml | ||
| CLAUDE.md | ||
| README.md | ||
shout
shout is kinda like really basic integration testing for your cli.
Write .shout files that look like shell sessions and run shout to test 'em.
Install
bun install -g @because/shout
Features
Everything you could ever ask for:
$ echo hello
hello
$ brew --version
Homebrew 5...
$ ls missing
ls: missing: No such file or directory
[1]
... matches anything — a whole line inline, or any number of lines on its own.
[1] after the expected output matches the exit code.
# at the start of a line is a comment — not executed, no output expected:
# start the server
$ my-server &
# now test it
$ curl localhost:8080
OK
If expected output starts with #, escape it with \#:
$ echo "#hashtag"
\#hashtag
# elsewhere in expected output is literal (not a comment).
Usage
$ shout test
...............
15 passed in 23ms
shout test runs code in a temp directory. -k/--keep keeps it around.
--update will modify .shout files to match reality, without running any tests.
Each line in a .shout file is run sequentially, unless --parallel is passed.
Directives
Directives go at the top of a .shout file, before any commands.
@env
Set environment variables for the test:
@env GREETING=hello
@env TARGET=world
$ echo "$GREETING $TARGET"
hello world
@setup
Prepend commands (and @env directives) from another .shout file:
@setup setup-shared.shout
Setup commands run first and their failures abort the test. Setup files cannot themselves contain @setup — no nesting. If both the setup file and the user file define the same @env, the user file wins.
@teardown
Run a cleanup command after all test commands, regardless of pass/fail:
@teardown rm -f "$SHOUT_PROJECT_DIR/data/test.db"
$ create-db && run-tests
...
Teardown failures produce warnings but don't affect test results. You can also put @teardown in setup files:
# setup.shout
export DB_URL=sqlite:data/test.db
@teardown rm -f "$SHOUT_PROJECT_DIR/data/test.db"
@def
Define a macro that substitutes a command by name:
@def greet echo "hello world"
$ greet
hello world
Use backslash \ for multi-line bodies. The body can start on the same line or on the next continuation line:
@def serve \
python3 -m http.server $PORT & \
sleep 0.5
$ serve
Macros defined in setup files are inherited. A user file @def with the same name overrides the setup version.
Usage: shout test [options] [files...]
Run .shout test files
Arguments:
files Files or directories to test
Options:
-u, --update Rewrite expected output in-place with actual output
-k, --keep Keep temp directories after run
--clean-env Start with empty environment
--path <path> Prepend <path> to PATH (repeatable)
--timeout <dur> Per-command timeout (default: "10s")
-t, --filter Only run files matching <pattern> (substring match)
-v, --verbose Print each command as it runs
--port-from <n> Auto-assign $PORT starting from n (default: "5400")
--parallel Run files in parallel
-h, --help display help for command
Environment Variables
Set automatically
| Variable | Value |
|---|---|
HOME |
Path to the temp directory created for the test |
SHOUT_DIR |
Same as HOME — the temp directory for the test |
SHOUT_SOURCE_DIR |
Directory containing the .shout file being run |
SHOUT_PROJECT_DIR |
The cwd where shout was invoked |
PORT |
Auto-assigned starting from 5400 (or the value of --port-from), increments per file. Not set if PORT is already defined via @env or @setup. |
Inherited
By default, the test shell inherits all environment variables from the parent process. Use --clean-env to start with an empty environment instead.
Modified
PATH is prepended with any directories passed via --path <path>.
User-defined
Use @env KEY=VALUE directives to set arbitrary variables. See Directives.
Print an example .shout file:
$ shout example
Editor Support
Neovim
The vim/ directory contains a Neovim plugin with syntax highlighting and commands (:ShoutRun, :ShoutUpdate, :ShoutRunAll).
lazy.nvim
{
dir = "~/path/to/shout/vim",
ft = "shout",
}
Manual
mkdir -p ~/.local/share/nvim/site/pack/shout/start
ln -s /path/to/shout/vim ~/.local/share/nvim/site/pack/shout/start/shout