shout test

This commit is contained in:
Chris Wanstrath 2026-03-10 10:15:16 -07:00
parent b04849cc5a
commit d2c24da7dc
4 changed files with 40 additions and 31 deletions

View File

@ -6,7 +6,7 @@ Transcript-based shell integration test runner. Bun + TypeScript.
- `bun test` — run unit tests
- `bunx tsc --noEmit` — type check
- `bun run src/cli/index.ts [files...]` — run shout CLI
- `bun run src/cli/index.ts test [files...]` — run shout CLI
## Architecture

View File

@ -33,21 +33,21 @@ ls: missing: No such file or directory
## Usage
```
$ shout
$ shout test
...............
15 passed in 23ms
```
`shout` runs code in a temp directory. `-k`/`--keep` keeps it around.
`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.
```
Usage: shout [options] [files...]
Usage: shout test [options] [files...]
shell output tester.
Run .shout test files
Arguments:
files Files or directories to test
@ -61,5 +61,10 @@ Options:
-v, --verbose Print each command as it runs
--parallel Run files in parallel
-h, --help display help for command
```
Print an example `.shout` file:
```
$ shout example
```

View File

@ -200,7 +200,7 @@
<section>
<h2>Run it</h2>
<pre><code><span class="prompt">$</span> <span class="cmd">shout</span>
<pre><code><span class="prompt">$</span> <span class="cmd">shout test</span>
<span class="pass">...............
15 passed</span> <span class="dim">in 23ms</span></code></pre>
<p>Each file gets a fresh temp directory and its own <code>/bin/sh</code> session. State carries between commands within a file.</p>
@ -208,16 +208,16 @@
<section>
<h2>Update expectations</h2>
<pre><code><span class="prompt">$</span> <span class="cmd">shout --update</span></code></pre>
<pre><code><span class="prompt">$</span> <span class="cmd">shout test --update</span></code></pre>
<p>Rewrites your <code>.shout</code> files with the actual output. No more copy-pasting from the terminal.</p>
</section>
<section>
<h2>Usage</h2>
<pre><code><span class="prompt">$</span> <span class="cmd">shout --help</span>
<span class="output">Usage: shout [options] [files...]
<pre><code><span class="prompt">$</span> <span class="cmd">shout test --help</span>
<span class="output">Usage: shout test [options] [files...]
shell output tester.
Run .shout test files
Arguments:
files Files or directories to test

View File

@ -46,6 +46,10 @@ async function findShoutFiles(paths: string[]): Promise<string[]> {
program
.name("shout")
.description("$ shell output tester")
program
.command("test")
.description("Run .shout test files")
.argument("[files...]", "Files or directories to test")
.option("-u, --update", "Rewrite expected output in-place with actual output")
.option("-k, --keep", "Keep temp directories after run")
@ -54,28 +58,7 @@ program
.option("--timeout <dur>", "Per-command timeout", "10s")
.option("-v, --verbose", "Print each command as it runs")
.option("--parallel", "Run files in parallel")
.option("--example", "Print an example .shout file and exit")
.action(async (fileArgs: string[], opts) => {
if (opts.example) {
console.log(`# Example .shout file
$ echo hello
hello
$ echo "one"; echo "two"; echo "three"
one
...
three
$ cat nonexistent
cat: nonexistent: ...
[1]
$ true
[0]`)
process.exit(0)
}
const timeoutMs = parseDuration(opts.timeout)
const paths = fileArgs.length > 0 ? fileArgs : ["."]
const files = await findShoutFiles(paths)
@ -171,4 +154,25 @@ $ true
process.exit(failures.length > 0 ? 1 : 0)
})
program
.command("example")
.description("Print an example .shout file")
.action(() => {
console.log(`# Example .shout file
$ echo hello
hello
$ echo "one"; echo "two"; echo "three"
one
...
three
$ cat nonexistent
cat: nonexistent: ...
[1]
$ true
[0]`)
})
program.parse()