shout/README.md

206 lines
4.4 KiB
Markdown

# 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](#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
```lua
{
dir = "~/path/to/shout/vim",
ft = "shout",
}
```
#### Manual
```sh
mkdir -p ~/.local/share/nvim/site/pack/shout/start
ln -s /path/to/shout/vim ~/.local/share/nvim/site/pack/shout/start/shout
```